Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 324 #325

Merged
merged 2 commits into from
Jan 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,12 +213,12 @@
//! }
//! ```
//!
//! - `name`: `[name = "name"]`
//! - On top level: `App::new("name")`.
//! - `name`: `[name = expr]`
//! - On top level: `App::new(expr)`.
//!
//! The binary name displayed in help messages. Defaults to the crate name given by Cargo.
//!
//! - On field-level: `Arg::with_name("name")`.
//! - On field-level: `Arg::with_name(expr)`.
//!
//! The name for the argument the field stands for, this name appears in help messages.
//! Defaults to a name, deduced from a field, see also
Expand Down
71 changes: 32 additions & 39 deletions structopt-derive/src/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ pub enum CasingStyle {
#[derive(Clone)]
pub enum Name {
Derived(Ident),
Assigned(LitStr),
Assigned(TokenStream),
}

#[derive(Clone)]
Expand Down Expand Up @@ -193,11 +193,11 @@ impl CasingStyle {
}

impl Name {
pub fn translate(self, style: CasingStyle) -> LitStr {
pub fn translate(self, style: CasingStyle) -> TokenStream {
use CasingStyle::*;

match self {
Name::Assigned(lit) => lit,
Name::Assigned(tokens) => tokens,
Name::Derived(ident) => {
let s = ident.unraw().to_string();
let s = match style {
Expand All @@ -208,7 +208,7 @@ impl Name {
Snake => s.to_snake_case(),
Verbatim => s,
};
LitStr::new(&s, ident.span())
quote_spanned!(ident.span()=> #s)
}
}
}
Expand Down Expand Up @@ -247,13 +247,13 @@ impl Attrs {
}
}

/// push `.method("str literal")`
fn push_str_method(&mut self, name: Sp<String>, arg: Sp<String>) {
if *name == "name" {
self.name = Name::Assigned(arg.as_lit());
fn push_method(&mut self, name: Ident, arg: impl ToTokens) {
if name == "name" {
self.name = Name::Assigned(quote!(#arg));
} else if name == "version" {
self.version = Some(Method::new(name, quote!(#arg)));
} else {
self.methods
.push(Method::new(name.as_ident(), quote!(#arg)))
self.methods.push(Method::new(name, quote!(#arg)))
}
}

Expand All @@ -263,17 +263,11 @@ impl Attrs {
for attr in parse_structopt_attributes(attrs) {
match attr {
Short(ident) | Long(ident) => {
self.push_str_method(
ident.into(),
self.name.clone().translate(*self.casing).into(),
);
self.push_method(ident, self.name.clone().translate(*self.casing));
}

Env(ident) => {
self.push_str_method(
ident.into(),
self.name.clone().translate(*self.env_casing).into(),
);
self.push_method(ident, self.name.clone().translate(*self.env_casing));
}

Subcommand(ident) => {
Expand Down Expand Up @@ -336,16 +330,18 @@ impl Attrs {
}

Version(ident, version) => {
self.version = Some(Method::new(ident, quote!(#version)))
self.push_method(ident, version);
}

NameLitStr(name, lit) => {
self.push_str_method(name.into(), lit.into());
self.push_method(name, lit);
}

NameExpr(name, expr) => self.methods.push(Method::new(name, quote!(#expr))),
NameExpr(name, expr) => {
self.push_method(name, expr);
}

MethodCall(name, args) => self.methods.push(Method::new(name, quote!(#(#args),*))),
MethodCall(name, args) => self.push_method(name, quote!(#(#args),*)),

RenameAll(_, casing_lit) => {
self.casing = CasingStyle::from_lit(casing_lit);
Expand Down Expand Up @@ -567,27 +563,12 @@ impl Attrs {

/// generate methods from attributes on top of struct or enum
pub fn top_level_methods(&self) -> TokenStream {
let version = match (&self.no_version, &self.version) {
(Some(no_version), Some(_)) => abort!(
no_version.span(),
"`no_version` and `version = \"version\"` can't be used together"
),

(None, Some(m)) => m.to_token_stream(),

(None, None) => std::env::var("CARGO_PKG_VERSION")
.map(|version| quote!( .version(#version) ))
.unwrap_or_default(),

(Some(_), None) => quote!(),
};

let author = &self.author;
let about = &self.about;
let methods = &self.methods;
let doc_comment = &self.doc_comment;

quote!( #(#doc_comment)* #author #version #about #(#methods)* )
quote!( #(#doc_comment)* #author #about #(#methods)* )
}

/// generate methods on top of a field
Expand All @@ -597,7 +578,19 @@ impl Attrs {
quote!( #(#doc_comment)* #(#methods)* )
}

pub fn cased_name(&self) -> LitStr {
pub fn version(&self) -> TokenStream {
match (&self.no_version, &self.version) {
(None, Some(m)) => m.to_token_stream(),

(None, None) => std::env::var("CARGO_PKG_VERSION")
.map(|version| quote!( .version(#version) ))
.unwrap_or_default(),

_ => quote!(),
}
}

pub fn cased_name(&self) -> TokenStream {
self.name.clone().translate(*self.casing)
}

Expand Down
13 changes: 7 additions & 6 deletions structopt-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,12 @@ fn gen_augmentation(
});

let app_methods = parent_attribute.top_level_methods();
let version = parent_attribute.version();
quote! {{
let #app_var = #app_var#app_methods;
#( #args )*
#subcmd
#app_var
#app_var#version
}}
}

Expand Down Expand Up @@ -382,7 +383,7 @@ fn gen_clap(attrs: &[Attribute]) -> GenOutput {
let attrs = Attrs::from_struct(
Span::call_site(),
attrs,
Name::Assigned(LitStr::new(&name, Span::call_site())),
Name::Assigned(quote!(#name)),
None,
Sp::call_site(DEFAULT_CASING),
Sp::call_site(DEFAULT_ENV_CASING),
Expand Down Expand Up @@ -483,23 +484,23 @@ fn gen_augment_clap_enum(

let name = attrs.cased_name();
let from_attrs = attrs.top_level_methods();

let version = attrs.version();
quote! {
.subcommand({
let #app_var = ::structopt::clap::SubCommand::with_name(#name);
let #app_var = #arg_block;
#app_var#from_attrs
#app_var#from_attrs#version
})
}
});

let app_methods = parent_attribute.top_level_methods();

let version = parent_attribute.version();
quote! {
fn augment_clap<'a, 'b>(
app: ::structopt::clap::App<'a, 'b>
) -> ::structopt::clap::App<'a, 'b> {
app #app_methods #( #subcommands )*
app #app_methods #( #subcommands )* #version
}
}
}
Expand Down
10 changes: 0 additions & 10 deletions structopt-derive/src/spanned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,6 @@ impl<T> Sp<T> {
}
}

impl<T: ToString> Sp<T> {
pub fn as_ident(&self) -> Ident {
Ident::new(&self.to_string(), self.span)
}

pub fn as_lit(&self) -> LitStr {
LitStr::new(&self.to_string(), self.span)
}
}

impl<T> Deref for Sp<T> {
type Target = T;

Expand Down
30 changes: 28 additions & 2 deletions tests/issues.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// https://github.com/TeXitoi/structopt/issues/151
// https://github.com/TeXitoi/structopt/issues/289
// https://github.com/TeXitoi/structopt/issues/{NUMBER}

mod utils;
use utils::*;

use structopt::StructOpt;

#[test]
fn issue_151() {
Expand Down Expand Up @@ -65,3 +69,25 @@ fn issue_289() {
.get_matches_from_safe(&["test", "some", "test"])
.is_ok());
}

#[test]
fn issue_324() {
fn my_version() -> &'static str {
"MY_VERSION"
}

#[derive(StructOpt)]
#[structopt(version = my_version())]
struct Opt {
#[structopt(subcommand)]
_cmd: Option<SubCommand>,
}

#[derive(StructOpt)]
enum SubCommand {
Start,
}

let help = get_long_help::<Opt>();
assert!(help.contains("MY_VERSION"));
}