Skip to content

Commit

Permalink
Default value for default_value (yeah, sounds awkward)
Browse files Browse the repository at this point in the history
  • Loading branch information
CreepySkeleton committed Feb 4, 2020
1 parent 5aafe9e commit cf11d46
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 11 deletions.
13 changes: 7 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,12 @@ exitcode = "1.1.2"
unicode-width = "0.1.4"
textwrap = "0.11"
indexmap = "1.0.1"
strsim = { version = "0.9.0", optional = true }
yaml-rust = { version = "0.4.1", optional = true }
atty = { version = "0.2.2", optional = true }
vec_map = { version = "0.8", optional = true }
term_size = { version = "1.0.0-beta1", optional = true }
strsim = { version = "0.9.0", optional = true }
yaml-rust = { version = "0.4.1", optional = true }
atty = { version = "0.2.2", optional = true }
vec_map = { version = "0.8", optional = true }
term_size = { version = "1.0.0-beta1", optional = true }
lazy_static = { version = "1", optional = true }
clap_derive = { path = "./clap_derive", version = "3.0.0-beta.1", optional = true }

[target.'cfg(not(windows))'.dependencies]
Expand All @@ -74,7 +75,7 @@ std = [] # support for no_std in a backwards-compatible way
suggestions = ["strsim"]
color = ["ansi_term", "atty"]
wrap_help = ["term_size", "textwrap/term_size"]
derive = ["clap_derive"]
derive = ["clap_derive", "lazy_static"]
yaml = ["yaml-rust"]
unstable = [] # for building with unstable clap features (doesn't require nightly Rust) (currently none)
nightly = [] # for building with unstable Rust features (currently none)
Expand Down
39 changes: 37 additions & 2 deletions clap_derive/src/derives/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use heck::{CamelCase, KebabCase, MixedCase, ShoutySnakeCase, SnakeCase};
use proc_macro2::{self, Span, TokenStream};
use proc_macro_error::abort;
use quote::{quote, quote_spanned, ToTokens};
use syn::{self, ext::IdentExt, spanned::Spanned, Ident, LitStr, MetaNameValue};
use syn::{self, ext::IdentExt, spanned::Spanned, Ident, LitStr, MetaNameValue, Type};

/// Default casing style for generated arguments.
pub const DEFAULT_CASING: CasingStyle = CasingStyle::Kebab;
Expand Down Expand Up @@ -87,6 +87,7 @@ pub struct Attrs {
name: Name,
casing: Sp<CasingStyle>,
env_casing: Sp<CasingStyle>,
ty: Option<Type>,
doc_comment: Vec<Method>,
methods: Vec<Method>,
parser: Sp<Parser>,
Expand Down Expand Up @@ -244,11 +245,13 @@ impl Attrs {
fn new(
default_span: Span,
name: Name,
ty: Option<Type>,
casing: Sp<CasingStyle>,
env_casing: Sp<CasingStyle>,
) -> Self {
Self {
name,
ty,
casing,
env_casing,
doc_comment: vec![],
Expand Down Expand Up @@ -314,6 +317,37 @@ impl Attrs {

VerbatimDocComment(ident) => self.verbatim_doc_comment = Some(ident),

DefaultValue(ident, lit) => {
let val = if let Some(lit) = lit {
quote!(#lit)
} else {
let ty = if let Some(ty) = self.ty.as_ref() {
ty
} else {
abort!(
ident.span(),
"#[structopt(default_value)] (without an argument) can be used \
only on field level";

note = "see \
https://docs.rs/structopt/0.3.5/structopt/#magical-methods")
};

quote_spanned!(ident.span()=> {
::clap::lazy_static::lazy_static! {
static ref DEFAULT_VALUE: &'static str = {
let val = <#ty as ::std::default::Default>::default();
let s = ::std::string::ToString::to_string(&val);
::std::boxed::Box::leak(s.into_boxed_str())
};
}
*DEFAULT_VALUE
})
};

self.methods.push(Method::new(ident, val));
}

About(ident, about) => {
self.about = Method::from_lit_or_env(ident, about, "CARGO_PKG_DESCRIPTION");
}
Expand Down Expand Up @@ -379,7 +413,7 @@ impl Attrs {
argument_casing: Sp<CasingStyle>,
env_casing: Sp<CasingStyle>,
) -> Self {
let mut res = Self::new(span, name, argument_casing, env_casing);
let mut res = Self::new(span, name, None, argument_casing, env_casing);
res.push_attrs(attrs);
res.push_doc_comment(attrs, "about");

Expand All @@ -406,6 +440,7 @@ impl Attrs {
let mut res = Self::new(
field.span(),
Name::Derived(name.clone()),
Some(field.ty.clone()),
struct_casing,
env_casing,
);
Expand Down
3 changes: 3 additions & 0 deletions clap_derive/src/derives/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub enum ClapAttr {
// ident [= "string literal"]
About(Ident, Option<LitStr>),
Author(Ident, Option<LitStr>),
DefaultValue(Ident, Option<LitStr>),

// ident = "string literal"
Version(Ident, LitStr),
Expand Down Expand Up @@ -89,6 +90,7 @@ impl Parse for ClapAttr {
match &*name_str {
"rename_all" => Ok(RenameAll(name, lit)),
"rename_all_env" => Ok(RenameAllEnv(name, lit)),
"default_value" => Ok(DefaultValue(name, Some(lit))),

"version" => {
check_empty_lit("version");
Expand Down Expand Up @@ -187,6 +189,7 @@ impl Parse for ClapAttr {
"no_version" => Ok(NoVersion(name)),
"verbatim_doc_comment" => Ok(VerbatimDocComment(name)),

"default_value" => Ok(DefaultValue(name, None)),
"about" => (Ok(About(name, None))),
"author" => (Ok(Author(name, None))),

Expand Down
19 changes: 19 additions & 0 deletions clap_derive/tests/default_value.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use clap::Clap;

mod utils;

use utils::*;

#[test]
fn auto_default_value() {
#[derive(Clap, PartialEq, Debug)]
struct Opt {
#[clap(default_value)]
arg: i32,
}
assert_eq!(Opt { arg: 0 }, Opt::parse_from(&["test"]));
assert_eq!(Opt { arg: 1 }, Opt::parse_from(&["test", "1"]));

let help = get_long_help::<Opt>();
assert!(help.contains("[default: 0]"));
}
4 changes: 2 additions & 2 deletions clap_derive/tests/ui/raw.stderr
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
error: `#[clap(raw(...))` attributes are removed, they are replaced with raw methods

= help: if you meant to call `clap::Arg::raw()` method you should use bool literal, like `raw(true)` or `raw(false)`
= note: if you need to call `clap::Arg/App::case_insensitive` method you can do it like this: #[structopt(case_insensitive = true)]
= note: if you need to call `clap::Arg/App::case_insensitive` method you can do it like this: #[clap(case_insensitive = true)]

--> $DIR/raw.rs:13:12
|
Expand All @@ -11,7 +11,7 @@ error: `#[clap(raw(...))` attributes are removed, they are replaced with raw met
error: `#[clap(raw(...))` attributes are removed, they are replaced with raw methods

= help: if you meant to call `clap::Arg::raw()` method you should use bool literal, like `raw(true)` or `raw(false)`
= note: if you need to call `clap::Arg/App::requires_if` method you can do it like this: #[structopt(requires_if("one", "two"))]
= note: if you need to call `clap::Arg/App::requires_if` method you can do it like this: #[clap(requires_if("one", "two"))]

--> $DIR/raw.rs:19:12
|
Expand Down
6 changes: 5 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,11 @@ pub use yaml_rust::YamlLoader;

#[cfg(feature = "derive")]
#[cfg_attr(feature = "derive", doc(hidden))]
pub use clap_derive::*;
pub use clap_derive::{self, *};

#[cfg(feature = "derive")]
#[cfg_attr(feature = "derive", doc(hidden))]
pub use lazy_static;

use std::result::Result as StdResult;

Expand Down

0 comments on commit cf11d46

Please sign in to comment.