Skip to content

Commit

Permalink
Bugfix #324
Browse files Browse the repository at this point in the history
Fixes #324
  • Loading branch information
CreepySkeleton committed Jan 12, 2020
1 parent 35cefe8 commit 14eb5c4
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 60 deletions.
15 changes: 12 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 Expand Up @@ -1035,6 +1035,10 @@ pub trait StructOptInternal: StructOpt {
{
None
}

fn augment_version<'a, 'b>(app: clap::App<'a, 'b>) -> clap::App<'a, 'b> {
app
}
}

impl<T: StructOpt> StructOpt for Box<T> {
Expand Down Expand Up @@ -1062,4 +1066,9 @@ impl<T: StructOptInternal> StructOptInternal for Box<T> {
fn augment_clap<'a, 'b>(app: clap::App<'a, 'b>) -> clap::App<'a, 'b> {
<T as StructOptInternal>::augment_clap(app)
}

#[doc(hidden)]
fn augment_version<'a, 'b>(app: clap::App<'a, 'b>) -> clap::App<'a, 'b> {
<T as StructOptInternal>::augment_version(app)
}
}
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"));
}

0 comments on commit 14eb5c4

Please sign in to comment.