mirror of
https://github.com/clap-rs/clap
synced 2024-11-15 00:57:15 +00:00
update syn quote and proc_macro2 + fmt
This commit is contained in:
parent
87daa0bc8e
commit
01d34fb8d2
21 changed files with 769 additions and 383 deletions
|
@ -8,6 +8,9 @@ matrix:
|
|||
include:
|
||||
- rust: nightly
|
||||
env: FEATURES="--features nightly"
|
||||
|
||||
- rust: stable
|
||||
env: RUN=FMT
|
||||
before_script: rustup component add rustfmt-preview
|
||||
script: cargo fmt --all -- --write-mode diff
|
||||
script:
|
||||
- cargo test $FEATURES
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#[macro_use] extern crate structopt;
|
||||
#[macro_use]
|
||||
extern crate structopt;
|
||||
|
||||
use structopt::StructOpt;
|
||||
|
||||
|
@ -10,7 +11,7 @@ pub struct Foo {
|
|||
#[derive(Debug, StructOpt)]
|
||||
pub enum Command {
|
||||
#[structopt(name = "foo")]
|
||||
Foo(Foo)
|
||||
Foo(Foo),
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
//! Documentation can be added either through doc comments or the
|
||||
//! `about` attribute.
|
||||
|
||||
#[macro_use] extern crate structopt;
|
||||
#[macro_use]
|
||||
extern crate structopt;
|
||||
|
||||
use structopt::StructOpt;
|
||||
|
||||
|
@ -19,7 +20,7 @@ enum Opt {
|
|||
#[structopt(long = "all")]
|
||||
all: bool,
|
||||
#[structopt(default_value = "origin")]
|
||||
repository: String
|
||||
repository: String,
|
||||
},
|
||||
#[structopt(name = "add")]
|
||||
/// add files to the staging area
|
||||
|
@ -28,8 +29,8 @@ enum Opt {
|
|||
interactive: bool,
|
||||
#[structopt(short = "a")]
|
||||
all: bool,
|
||||
files: Vec<String>
|
||||
}
|
||||
files: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -14,8 +14,7 @@ fn vers_arg_group() -> ArgGroup<'static> {
|
|||
// As the attributes of the struct are executed before the struct
|
||||
// fields, we can't use .args(...), but we can use the group
|
||||
// attribute on the fields.
|
||||
ArgGroup::with_name("vers")
|
||||
.required(true)
|
||||
ArgGroup::with_name("vers").required(true)
|
||||
}
|
||||
|
||||
#[derive(StructOpt, Debug)]
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#[macro_use]
|
||||
extern crate structopt;
|
||||
|
||||
use structopt::StructOpt;
|
||||
use std::error::Error;
|
||||
use structopt::StructOpt;
|
||||
|
||||
fn parse_key_val<T, U>(s: &str) -> Result<(T, U), Box<Error>>
|
||||
where
|
||||
|
@ -11,7 +11,8 @@ where
|
|||
U: std::str::FromStr,
|
||||
U::Err: Error + 'static,
|
||||
{
|
||||
let pos = s.find('=').ok_or_else(|| format!("invalid KEY=value: no `=` found in `{}`", s))?;
|
||||
let pos = s.find('=')
|
||||
.ok_or_else(|| format!("invalid KEY=value: no `=` found in `{}`", s))?;
|
||||
Ok((s[..pos].parse()?, s[pos + 1..].parse()?))
|
||||
}
|
||||
|
||||
|
|
|
@ -7,8 +7,7 @@ use structopt::clap::AppSettings;
|
|||
#[derive(StructOpt, Debug)]
|
||||
#[structopt(name = "no_version", about = "", version = "", author = "",
|
||||
raw(global_settings = "&[AppSettings::DisableVersion]"))]
|
||||
struct Opt {
|
||||
}
|
||||
struct Opt {}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::from_args();
|
||||
|
|
|
@ -411,7 +411,10 @@ pub trait StructOpt {
|
|||
|
||||
/// Gets the struct from the command line arguments. Print the
|
||||
/// error message and quit the program in case of failure.
|
||||
fn from_args() -> Self where Self: Sized {
|
||||
fn from_args() -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Self::from_clap(&Self::clap().get_matches())
|
||||
}
|
||||
|
||||
|
@ -421,7 +424,7 @@ pub trait StructOpt {
|
|||
where
|
||||
Self: Sized,
|
||||
I: IntoIterator,
|
||||
I::Item: Into<OsString> + Clone
|
||||
I::Item: Into<OsString> + Clone,
|
||||
{
|
||||
Self::from_clap(&Self::clap().get_matches_from(iter))
|
||||
}
|
||||
|
@ -435,7 +438,7 @@ pub trait StructOpt {
|
|||
where
|
||||
Self: Sized,
|
||||
I: IntoIterator,
|
||||
I::Item: Into<OsString> + Clone
|
||||
I::Item: Into<OsString> + Clone,
|
||||
{
|
||||
Ok(Self::from_clap(&Self::clap().get_matches_from_safe(iter)?))
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@ license = "Apache-2.0/MIT"
|
|||
travis-ci = { repository = "TeXitoi/structopt" }
|
||||
|
||||
[dependencies]
|
||||
syn = "0.13"
|
||||
quote = "0.5"
|
||||
proc-macro2 = "0.3"
|
||||
syn = "0.14"
|
||||
quote = "0.6"
|
||||
proc-macro2 = "0.4"
|
||||
|
||||
[features]
|
||||
nightly = ["proc-macro2/nightly"]
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use std::{env, mem};
|
||||
use quote::Tokens;
|
||||
use syn::{self, Attribute, MetaNameValue, MetaList, LitStr, TypePath};
|
||||
use syn::Type::Path;
|
||||
use syn::{self, Attribute, Ident, LitStr, MetaList, MetaNameValue, TypePath};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum Kind {
|
||||
|
@ -28,14 +28,14 @@ pub enum Ty {
|
|||
pub struct Attrs {
|
||||
name: String,
|
||||
methods: Vec<Method>,
|
||||
parser: (Parser, Tokens),
|
||||
parser: (Parser, TokenStream),
|
||||
has_custom_parser: bool,
|
||||
kind: Kind,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
struct Method {
|
||||
name: String,
|
||||
args: Tokens,
|
||||
args: TokenStream,
|
||||
}
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Parser {
|
||||
|
@ -54,7 +54,7 @@ impl ::std::str::FromStr for Parser {
|
|||
"from_os_str" => Ok(Parser::FromOsStr),
|
||||
"try_from_os_str" => Ok(Parser::TryFromOsStr),
|
||||
"from_occurrences" => Ok(Parser::FromOccurrences),
|
||||
_ => Err(format!("unsupported parser {}", s))
|
||||
_ => Err(format!("unsupported parser {}", s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,10 +73,7 @@ impl Attrs {
|
|||
match (name, arg) {
|
||||
("about", "") | ("version", "") | ("author", "") => {
|
||||
let methods = mem::replace(&mut self.methods, vec![]);
|
||||
self.methods = methods
|
||||
.into_iter()
|
||||
.filter(|m| m.name != name)
|
||||
.collect();
|
||||
self.methods = methods.into_iter().filter(|m| m.name != name).collect();
|
||||
}
|
||||
("name", new_name) => self.name = new_name.into(),
|
||||
(name, arg) => self.methods.push(Method {
|
||||
|
@ -86,22 +83,23 @@ impl Attrs {
|
|||
}
|
||||
}
|
||||
fn push_attrs(&mut self, attrs: &[Attribute]) {
|
||||
use Lit::*;
|
||||
use Meta::*;
|
||||
use NestedMeta::*;
|
||||
use Lit::*;
|
||||
|
||||
let iter = attrs.iter()
|
||||
let iter = attrs
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
let path = &attr.path;
|
||||
match quote!(#path) == quote!(structopt) {
|
||||
match quote!(#path).to_string() == "structopt" {
|
||||
true => Some(
|
||||
attr.interpret_meta()
|
||||
.expect(&format!("invalid structopt syntax: {}", quote!(attr)))
|
||||
.expect(&format!("invalid structopt syntax: {}", quote!(attr))),
|
||||
),
|
||||
false => None,
|
||||
}
|
||||
}).
|
||||
flat_map(|m| match m {
|
||||
})
|
||||
.flat_map(|m| match m {
|
||||
List(l) => l.nested,
|
||||
tokens => panic!("unsupported syntax: {}", quote!(#tokens).to_string()),
|
||||
})
|
||||
|
@ -110,46 +108,55 @@ impl Attrs {
|
|||
ref tokens => panic!("unsupported syntax: {}", quote!(#tokens).to_string()),
|
||||
});
|
||||
for attr in iter {
|
||||
match attr {
|
||||
NameValue(MetaNameValue { ident, lit: Str(ref value), .. }) =>
|
||||
self.push_str_method(ident.as_ref(), &value.value()),
|
||||
NameValue(MetaNameValue { ident, lit, .. }) => {
|
||||
self.methods.push(Method {
|
||||
name: ident.to_string(),
|
||||
args: quote!(#lit),
|
||||
})
|
||||
}
|
||||
List(MetaList { ident, ref nested, .. }) if ident == "parse" => {
|
||||
match &attr {
|
||||
NameValue(MetaNameValue {
|
||||
ident,
|
||||
lit: Str(value),
|
||||
..
|
||||
}) => self.push_str_method(&ident.to_string(), &value.value()),
|
||||
NameValue(MetaNameValue { ident, lit, .. }) => self.methods.push(Method {
|
||||
name: ident.to_string(),
|
||||
args: quote!(#lit),
|
||||
}),
|
||||
List(MetaList { ident, nested, .. }) if ident == "parse" => {
|
||||
if nested.len() != 1 {
|
||||
panic!("parse must have exactly one argument");
|
||||
}
|
||||
self.has_custom_parser = true;
|
||||
self.parser = match nested[0] {
|
||||
Meta(NameValue(MetaNameValue { ident, lit: Str(ref v), .. })) => {
|
||||
self.parser = match &nested[0] {
|
||||
Meta(NameValue(MetaNameValue {
|
||||
ident, lit: Str(v), ..
|
||||
})) => {
|
||||
let function: syn::Path = v.parse().expect("parser function path");
|
||||
let parser = ident.as_ref().parse().unwrap();
|
||||
let parser = ident.to_string().parse().unwrap();
|
||||
(parser, quote!(#function))
|
||||
}
|
||||
Meta(Word(ref i)) => {
|
||||
use Parser::*;
|
||||
let parser = i.as_ref().parse().unwrap();
|
||||
let parser = i.to_string().parse().unwrap();
|
||||
let function = match parser {
|
||||
FromStr => quote!(::std::convert::From::from),
|
||||
TryFromStr => quote!(::std::str::FromStr::from_str),
|
||||
FromOsStr => quote!(::std::convert::From::from),
|
||||
TryFromOsStr => panic!("cannot omit parser function name with `try_from_os_str`"),
|
||||
FromOccurrences => quote!({|v| v as _}),
|
||||
TryFromOsStr => panic!(
|
||||
"cannot omit parser function name with `try_from_os_str`"
|
||||
),
|
||||
FromOccurrences => quote!({ |v| v as _ }),
|
||||
};
|
||||
(parser, function)
|
||||
}
|
||||
ref l @ _ => panic!("unknown value parser specification: {}", quote!(#l)),
|
||||
};
|
||||
}
|
||||
List(MetaList { ident, ref nested, .. }) if ident == "raw" => {
|
||||
List(MetaList {
|
||||
ident, ref nested, ..
|
||||
}) if ident == "raw" =>
|
||||
{
|
||||
for method in nested {
|
||||
match *method {
|
||||
Meta(NameValue(MetaNameValue { ident, lit: Str(ref v), .. })) =>
|
||||
self.push_raw_method(ident.as_ref(), v),
|
||||
match method {
|
||||
Meta(NameValue(MetaNameValue {
|
||||
ident, lit: Str(v), ..
|
||||
})) => self.push_raw_method(&ident.to_string(), v),
|
||||
ref mi @ _ => panic!("unsupported raw entry: {}", quote!(#mi)),
|
||||
}
|
||||
}
|
||||
|
@ -160,33 +167,41 @@ impl Attrs {
|
|||
Word(ref w) if w == "flatten" => {
|
||||
self.set_kind(Kind::FlattenStruct);
|
||||
}
|
||||
ref i @ List(..) | ref i @ Word(..) =>
|
||||
panic!("unsupported option: {}", quote!(#i)),
|
||||
ref i @ List(..) | ref i @ Word(..) => panic!("unsupported option: {}", quote!(#i)),
|
||||
}
|
||||
}
|
||||
}
|
||||
fn push_raw_method(&mut self, name: &str, args: &LitStr) {
|
||||
let ts: ::proc_macro2::TokenStream = args.value().parse()
|
||||
.expect(&format!("bad parameter {} = {}: the parameter must be valid rust code", name, quote!(#args)));
|
||||
let ts: TokenStream = args.value().parse().expect(&format!(
|
||||
"bad parameter {} = {}: the parameter must be valid rust code",
|
||||
name,
|
||||
quote!(#args)
|
||||
));
|
||||
self.methods.push(Method {
|
||||
name: name.to_string(),
|
||||
args: quote!(#(#ts)*),
|
||||
})
|
||||
}
|
||||
fn push_doc_comment(&mut self, attrs: &[Attribute], name: &str) {
|
||||
let doc_comments: Vec<_> = attrs.iter()
|
||||
let doc_comments: Vec<_> = attrs
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
let path = &attr.path;
|
||||
match quote!(#path) == quote!(doc) {
|
||||
match quote!(#path).to_string() == "doc" {
|
||||
true => attr.interpret_meta(),
|
||||
false => None,
|
||||
}
|
||||
})
|
||||
.filter_map(|attr| {
|
||||
use Meta::*;
|
||||
use Lit::*;
|
||||
if let NameValue(MetaNameValue { ident, lit: Str(s), .. }) = attr {
|
||||
if ident != "doc" { return None; }
|
||||
use Meta::*;
|
||||
if let NameValue(MetaNameValue {
|
||||
ident, lit: Str(s), ..
|
||||
}) = attr
|
||||
{
|
||||
if ident != "doc" {
|
||||
return None;
|
||||
}
|
||||
let value = s.value();
|
||||
let text = value
|
||||
.trim_left_matches("//!")
|
||||
|
@ -197,7 +212,7 @@ impl Attrs {
|
|||
.trim();
|
||||
if text.is_empty() {
|
||||
Some("\n\n".to_string())
|
||||
} else{
|
||||
} else {
|
||||
Some(text.to_string())
|
||||
}
|
||||
} else {
|
||||
|
@ -205,7 +220,9 @@ impl Attrs {
|
|||
}
|
||||
})
|
||||
.collect();
|
||||
if doc_comments.is_empty() { return; }
|
||||
if doc_comments.is_empty() {
|
||||
return;
|
||||
}
|
||||
let arg = doc_comments
|
||||
.join(" ")
|
||||
.split('\n')
|
||||
|
@ -224,7 +241,8 @@ impl Attrs {
|
|||
("about", "CARGO_PKG_DESCRIPTION"),
|
||||
("author", "CARGO_PKG_AUTHORS"),
|
||||
];
|
||||
attrs_with_env.iter()
|
||||
attrs_with_env
|
||||
.iter()
|
||||
.filter_map(|&(m, v)| env::var(v).ok().and_then(|arg| Some((m, arg))))
|
||||
.filter(|&(_, ref arg)| !arg.is_empty())
|
||||
.for_each(|(name, arg)| {
|
||||
|
@ -247,8 +265,12 @@ impl Attrs {
|
|||
}
|
||||
}
|
||||
fn ty_from_field(ty: &syn::Type) -> Ty {
|
||||
if let Path(TypePath { path: syn::Path { ref segments, .. }, .. }) = *ty {
|
||||
match segments.iter().last().unwrap().ident.as_ref() {
|
||||
if let Path(TypePath {
|
||||
path: syn::Path { ref segments, .. },
|
||||
..
|
||||
}) = *ty
|
||||
{
|
||||
match segments.iter().last().unwrap().ident.to_string().as_str() {
|
||||
"bool" => Ty::Bool,
|
||||
"Option" => Ty::Option,
|
||||
"Vec" => Ty::Vec,
|
||||
|
@ -259,7 +281,7 @@ impl Attrs {
|
|||
}
|
||||
}
|
||||
pub fn from_field(field: &syn::Field) -> Attrs {
|
||||
let name = field.ident.as_ref().unwrap().as_ref().to_string();
|
||||
let name = field.ident.as_ref().unwrap().to_string();
|
||||
let mut res = Self::new(name);
|
||||
res.push_doc_comment(&field.attrs, "help");
|
||||
res.push_attrs(&field.attrs);
|
||||
|
@ -298,7 +320,7 @@ impl Attrs {
|
|||
if res.has_method("required") {
|
||||
panic!("required is meaningless for bool")
|
||||
}
|
||||
},
|
||||
}
|
||||
Ty::Option => {
|
||||
if res.has_method("default_value") {
|
||||
panic!("default_value is meaningless for Option")
|
||||
|
@ -306,7 +328,7 @@ impl Attrs {
|
|||
if res.has_method("required") {
|
||||
panic!("required is meaningless for Option")
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
res.kind = Kind::Arg(ty);
|
||||
|
@ -325,9 +347,9 @@ impl Attrs {
|
|||
pub fn has_method(&self, method: &str) -> bool {
|
||||
self.methods.iter().find(|m| m.name == method).is_some()
|
||||
}
|
||||
pub fn methods(&self) -> Tokens {
|
||||
pub fn methods(&self) -> TokenStream {
|
||||
let methods = self.methods.iter().map(|&Method { ref name, ref args }| {
|
||||
let name: ::syn::Ident = name.as_str().into();
|
||||
let name = Ident::new(&name, Span::call_site());
|
||||
quote!( .#name(#args) )
|
||||
});
|
||||
quote!( #(#methods)* )
|
||||
|
@ -335,7 +357,7 @@ impl Attrs {
|
|||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
pub fn parser(&self) -> &(Parser, Tokens) {
|
||||
pub fn parser(&self) -> &(Parser, TokenStream) {
|
||||
&self.parser
|
||||
}
|
||||
pub fn kind(&self) -> Kind {
|
||||
|
|
|
@ -18,15 +18,15 @@ extern crate proc_macro2;
|
|||
|
||||
mod attrs;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use syn::*;
|
||||
use attrs::{Attrs, Kind, Parser, Ty};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::token::{Comma};
|
||||
use attrs::{Attrs, Parser, Kind, Ty};
|
||||
use syn::token::Comma;
|
||||
use syn::*;
|
||||
|
||||
/// Generates the `StructOpt` impl.
|
||||
#[proc_macro_derive(StructOpt, attributes(structopt))]
|
||||
pub fn structopt(input: TokenStream) -> TokenStream {
|
||||
pub fn structopt(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input: DeriveInput = syn::parse(input).unwrap();
|
||||
let gen = impl_structopt(&input);
|
||||
gen.into()
|
||||
|
@ -34,16 +34,19 @@ pub fn structopt(input: TokenStream) -> TokenStream {
|
|||
|
||||
fn sub_type(t: &syn::Type) -> Option<&syn::Type> {
|
||||
let segs = match *t {
|
||||
syn::Type::Path(TypePath { path: syn::Path { ref segments, .. }, .. }) => segments,
|
||||
syn::Type::Path(TypePath {
|
||||
path: syn::Path { ref segments, .. },
|
||||
..
|
||||
}) => segments,
|
||||
_ => return None,
|
||||
};
|
||||
match *segs.iter().last().unwrap() {
|
||||
PathSegment {
|
||||
arguments: PathArguments::AngleBracketed(
|
||||
AngleBracketedGenericArguments { ref args, .. }
|
||||
),
|
||||
arguments:
|
||||
PathArguments::AngleBracketed(AngleBracketedGenericArguments { ref args, .. }),
|
||||
..
|
||||
} if args.len() == 1 => {
|
||||
} if args.len() == 1 =>
|
||||
{
|
||||
if let GenericArgument::Type(ref ty) = args[0] {
|
||||
Some(ty)
|
||||
} else {
|
||||
|
@ -56,19 +59,24 @@ fn sub_type(t: &syn::Type) -> Option<&syn::Type> {
|
|||
|
||||
/// Generate a block of code to add arguments/subcommands corresponding to
|
||||
/// the `fields` to an app.
|
||||
fn gen_augmentation(fields: &Punctuated<Field, Comma>, app_var: &Ident) -> quote::Tokens {
|
||||
let subcmds: Vec<quote::Tokens> = fields.iter()
|
||||
fn gen_augmentation(fields: &Punctuated<Field, Comma>, app_var: &Ident) -> TokenStream {
|
||||
let subcmds: Vec<_> = fields
|
||||
.iter()
|
||||
.filter_map(|field| {
|
||||
let attrs = Attrs::from_field(&field);
|
||||
if let Kind::Subcommand(ty) = attrs.kind() {
|
||||
let subcmd_type = match (ty, sub_type(&field.ty)) {
|
||||
(Ty::Option, Some(sub_type)) => sub_type,
|
||||
_ => &field.ty
|
||||
_ => &field.ty,
|
||||
};
|
||||
let required = if ty == Ty::Option {
|
||||
quote!()
|
||||
} else {
|
||||
quote!( let #app_var = #app_var.setting(::structopt::clap::AppSettings::SubcommandRequiredElseHelp); )
|
||||
quote! {
|
||||
let #app_var = #app_var.setting(
|
||||
::structopt::clap::AppSettings::SubcommandRequiredElseHelp
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Some(quote!{
|
||||
|
@ -81,64 +89,70 @@ fn gen_augmentation(fields: &Punctuated<Field, Comma>, app_var: &Ident) -> quote
|
|||
})
|
||||
.collect();
|
||||
|
||||
assert!(subcmds.len() <= 1, "cannot have more than one nested subcommand");
|
||||
assert!(
|
||||
subcmds.len() <= 1,
|
||||
"cannot have more than one nested subcommand"
|
||||
);
|
||||
|
||||
let args = fields.iter()
|
||||
.filter_map(|field| {
|
||||
let attrs = Attrs::from_field(field);
|
||||
match attrs.kind() {
|
||||
Kind::Subcommand(_) => None,
|
||||
Kind::FlattenStruct => {
|
||||
let ty = &field.ty;
|
||||
Some(quote! {
|
||||
let #app_var = <#ty>::augment_clap(#app_var);
|
||||
let #app_var = if <#ty>::is_subcommand() {
|
||||
#app_var.setting(::structopt::clap::AppSettings::SubcommandRequiredElseHelp)
|
||||
} else {
|
||||
#app_var
|
||||
};
|
||||
})
|
||||
}
|
||||
Kind::Arg(ty) => {
|
||||
let convert_type = match ty {
|
||||
Ty::Vec | Ty::Option => sub_type(&field.ty).unwrap_or(&field.ty),
|
||||
_ => &field.ty,
|
||||
let args = fields.iter().filter_map(|field| {
|
||||
let attrs = Attrs::from_field(field);
|
||||
match attrs.kind() {
|
||||
Kind::Subcommand(_) => None,
|
||||
Kind::FlattenStruct => {
|
||||
let ty = &field.ty;
|
||||
Some(quote! {
|
||||
let #app_var = <#ty>::augment_clap(#app_var);
|
||||
let #app_var = if <#ty>::is_subcommand() {
|
||||
#app_var.setting(::structopt::clap::AppSettings::SubcommandRequiredElseHelp)
|
||||
} else {
|
||||
#app_var
|
||||
};
|
||||
|
||||
let occurences = attrs.parser().0 == Parser::FromOccurrences;
|
||||
|
||||
let validator = match *attrs.parser() {
|
||||
(Parser::TryFromStr, ref f) => quote! {
|
||||
.validator(|s| {
|
||||
#f(&s)
|
||||
.map(|_: #convert_type| ())
|
||||
.map_err(|e| e.to_string())
|
||||
})
|
||||
},
|
||||
(Parser::TryFromOsStr, ref f) => quote! {
|
||||
.validator_os(|s| #f(&s).map(|_: #convert_type| ()))
|
||||
},
|
||||
_ => quote!(),
|
||||
};
|
||||
|
||||
let modifier = match ty {
|
||||
Ty::Bool => quote!( .takes_value(false).multiple(false) ),
|
||||
Ty::Option => quote!( .takes_value(true).multiple(false) #validator ),
|
||||
Ty::Vec => quote!( .takes_value(true).multiple(true) #validator ),
|
||||
Ty::Other if occurences => quote!( .takes_value(false).multiple(true) ),
|
||||
Ty::Other => {
|
||||
let required = !attrs.has_method("default_value");
|
||||
quote!( .takes_value(true).multiple(false).required(#required) #validator )
|
||||
},
|
||||
};
|
||||
let methods = attrs.methods();
|
||||
let name = attrs.name();
|
||||
Some(quote!{
|
||||
let #app_var = #app_var.arg(::structopt::clap::Arg::with_name(#name)#modifier#methods);
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
Kind::Arg(ty) => {
|
||||
let convert_type = match ty {
|
||||
Ty::Vec | Ty::Option => sub_type(&field.ty).unwrap_or(&field.ty),
|
||||
_ => &field.ty,
|
||||
};
|
||||
|
||||
let occurences = attrs.parser().0 == Parser::FromOccurrences;
|
||||
|
||||
let validator = match *attrs.parser() {
|
||||
(Parser::TryFromStr, ref f) => quote! {
|
||||
.validator(|s| {
|
||||
#f(&s)
|
||||
.map(|_: #convert_type| ())
|
||||
.map_err(|e| e.to_string())
|
||||
})
|
||||
},
|
||||
(Parser::TryFromOsStr, ref f) => quote! {
|
||||
.validator_os(|s| #f(&s).map(|_: #convert_type| ()))
|
||||
},
|
||||
_ => quote!(),
|
||||
};
|
||||
|
||||
let modifier = match ty {
|
||||
Ty::Bool => quote!( .takes_value(false).multiple(false) ),
|
||||
Ty::Option => quote!( .takes_value(true).multiple(false) #validator ),
|
||||
Ty::Vec => quote!( .takes_value(true).multiple(true) #validator ),
|
||||
Ty::Other if occurences => quote!( .takes_value(false).multiple(true) ),
|
||||
Ty::Other => {
|
||||
let required = !attrs.has_method("default_value");
|
||||
quote!( .takes_value(true).multiple(false).required(#required) #validator )
|
||||
}
|
||||
};
|
||||
let methods = attrs.methods();
|
||||
let name = attrs.name();
|
||||
Some(quote!{
|
||||
let #app_var = #app_var.arg(
|
||||
::structopt::clap::Arg::with_name(#name)
|
||||
#modifier
|
||||
#methods
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
quote! {{
|
||||
#( #args )*
|
||||
|
@ -147,7 +161,7 @@ fn gen_augmentation(fields: &Punctuated<Field, Comma>, app_var: &Ident) -> quote
|
|||
}}
|
||||
}
|
||||
|
||||
fn gen_constructor(fields: &Punctuated<Field, Comma>) -> quote::Tokens {
|
||||
fn gen_constructor(fields: &Punctuated<Field, Comma>) -> TokenStream {
|
||||
let fields = fields.iter().map(|field| {
|
||||
let attrs = Attrs::from_field(field);
|
||||
let field_name = field.ident.as_ref().unwrap();
|
||||
|
@ -155,27 +169,30 @@ fn gen_constructor(fields: &Punctuated<Field, Comma>) -> quote::Tokens {
|
|||
Kind::Subcommand(ty) => {
|
||||
let subcmd_type = match (ty, sub_type(&field.ty)) {
|
||||
(Ty::Option, Some(sub_type)) => sub_type,
|
||||
_ => &field.ty
|
||||
_ => &field.ty,
|
||||
};
|
||||
let unwrapper = match ty {
|
||||
Ty::Option => quote!(),
|
||||
_ => quote!( .unwrap() )
|
||||
_ => quote!( .unwrap() ),
|
||||
};
|
||||
quote!(#field_name: <#subcmd_type>::from_subcommand(matches.subcommand())#unwrapper)
|
||||
}
|
||||
Kind::FlattenStruct => {
|
||||
quote!(#field_name: StructOpt::from_clap(matches))
|
||||
}
|
||||
Kind::FlattenStruct => quote!(#field_name: StructOpt::from_clap(matches)),
|
||||
Kind::Arg(ty) => {
|
||||
use Parser::*;
|
||||
let (value_of, values_of, parse) = match *attrs.parser() {
|
||||
(FromStr, ref f) => (quote!(value_of), quote!(values_of), f.clone()),
|
||||
(TryFromStr, ref f) =>
|
||||
(quote!(value_of), quote!(values_of), quote!(|s| #f(s).unwrap())),
|
||||
(FromOsStr, ref f) =>
|
||||
(quote!(value_of_os), quote!(values_of_os), f.clone()),
|
||||
(TryFromOsStr, ref f) =>
|
||||
(quote!(value_of_os), quote!(values_of_os), quote!(|s| #f(s).unwrap())),
|
||||
(TryFromStr, ref f) => (
|
||||
quote!(value_of),
|
||||
quote!(values_of),
|
||||
quote!(|s| #f(s).unwrap()),
|
||||
),
|
||||
(FromOsStr, ref f) => (quote!(value_of_os), quote!(values_of_os), f.clone()),
|
||||
(TryFromOsStr, ref f) => (
|
||||
quote!(value_of_os),
|
||||
quote!(values_of_os),
|
||||
quote!(|s| #f(s).unwrap()),
|
||||
),
|
||||
(FromOccurrences, ref f) => (quote!(occurrences_of), quote!(), f.clone()),
|
||||
};
|
||||
|
||||
|
@ -213,7 +230,7 @@ fn gen_constructor(fields: &Punctuated<Field, Comma>) -> quote::Tokens {
|
|||
}}
|
||||
}
|
||||
|
||||
fn gen_from_clap(struct_name: &Ident, fields: &Punctuated<Field, Comma>) -> quote::Tokens {
|
||||
fn gen_from_clap(struct_name: &Ident, fields: &Punctuated<Field, Comma>) -> TokenStream {
|
||||
let field_block = gen_constructor(fields);
|
||||
|
||||
quote! {
|
||||
|
@ -223,15 +240,17 @@ fn gen_from_clap(struct_name: &Ident, fields: &Punctuated<Field, Comma>) -> quot
|
|||
}
|
||||
}
|
||||
|
||||
fn gen_clap(attrs: &[Attribute]) -> quote::Tokens {
|
||||
let name = std::env::var("CARGO_PKG_NAME").ok().unwrap_or_else(String::default);
|
||||
fn gen_clap(attrs: &[Attribute]) -> TokenStream {
|
||||
let name = std::env::var("CARGO_PKG_NAME")
|
||||
.ok()
|
||||
.unwrap_or_else(String::default);
|
||||
let attrs = Attrs::from_struct(attrs, name);
|
||||
let name = attrs.name();
|
||||
let methods = attrs.methods();
|
||||
quote!(::structopt::clap::App::new(#name)#methods)
|
||||
}
|
||||
|
||||
fn gen_clap_struct(struct_attrs: &[Attribute]) -> quote::Tokens {
|
||||
fn gen_clap_struct(struct_attrs: &[Attribute]) -> TokenStream {
|
||||
let gen = gen_clap(struct_attrs);
|
||||
quote! {
|
||||
fn clap<'a, 'b>() -> ::structopt::clap::App<'a, 'b> {
|
||||
|
@ -241,17 +260,19 @@ fn gen_clap_struct(struct_attrs: &[Attribute]) -> quote::Tokens {
|
|||
}
|
||||
}
|
||||
|
||||
fn gen_augment_clap(fields: &Punctuated<Field, Comma>) -> quote::Tokens {
|
||||
let app_var: Ident = "app".into();
|
||||
fn gen_augment_clap(fields: &Punctuated<Field, Comma>) -> TokenStream {
|
||||
let app_var = Ident::new("app", Span::call_site());
|
||||
let augmentation = gen_augmentation(fields, &app_var);
|
||||
quote! {
|
||||
pub fn augment_clap<'a, 'b>(#app_var: ::structopt::clap::App<'a, 'b>) -> ::structopt::clap::App<'a, 'b> {
|
||||
pub fn augment_clap<'a, 'b>(
|
||||
#app_var: ::structopt::clap::App<'a, 'b>
|
||||
) -> ::structopt::clap::App<'a, 'b> {
|
||||
#augmentation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_clap_enum(enum_attrs: &[Attribute]) -> quote::Tokens {
|
||||
fn gen_clap_enum(enum_attrs: &[Attribute]) -> TokenStream {
|
||||
let gen = gen_clap(enum_attrs);
|
||||
quote! {
|
||||
fn clap<'a, 'b>() -> ::structopt::clap::App<'a, 'b> {
|
||||
|
@ -262,13 +283,13 @@ fn gen_clap_enum(enum_attrs: &[Attribute]) -> quote::Tokens {
|
|||
}
|
||||
}
|
||||
|
||||
fn gen_augment_clap_enum(variants: &Punctuated<Variant, Comma>) -> quote::Tokens {
|
||||
fn gen_augment_clap_enum(variants: &Punctuated<Variant, Comma>) -> TokenStream {
|
||||
use syn::Fields::*;
|
||||
|
||||
let subcommands = variants.iter().map(|variant| {
|
||||
let name = variant.ident.as_ref().to_string();
|
||||
let name = variant.ident.to_string();
|
||||
let attrs = Attrs::from_struct(&variant.attrs, name);
|
||||
let app_var: Ident = "subcommand".into();
|
||||
let app_var = Ident::new("subcommand", Span::call_site());
|
||||
let arg_block = match variant.fields {
|
||||
Named(ref fields) => gen_augmentation(&fields.named, &app_var),
|
||||
Unit => quote!( #app_var ),
|
||||
|
@ -278,7 +299,9 @@ fn gen_augment_clap_enum(variants: &Punctuated<Variant, Comma>) -> quote::Tokens
|
|||
{
|
||||
let #app_var = <#ty>::augment_clap(#app_var);
|
||||
if <#ty>::is_subcommand() {
|
||||
#app_var.setting(::structopt::clap::AppSettings::SubcommandRequiredElseHelp)
|
||||
#app_var.setting(
|
||||
::structopt::clap::AppSettings::SubcommandRequiredElseHelp
|
||||
)
|
||||
} else {
|
||||
#app_var
|
||||
}
|
||||
|
@ -300,13 +323,15 @@ fn gen_augment_clap_enum(variants: &Punctuated<Variant, Comma>) -> quote::Tokens
|
|||
});
|
||||
|
||||
quote! {
|
||||
pub fn augment_clap<'a, 'b>(app: ::structopt::clap::App<'a, 'b>) -> ::structopt::clap::App<'a, 'b> {
|
||||
pub fn augment_clap<'a, 'b>(
|
||||
app: ::structopt::clap::App<'a, 'b>
|
||||
) -> ::structopt::clap::App<'a, 'b> {
|
||||
app #( #subcommands )*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_from_clap_enum(name: &Ident) -> quote::Tokens {
|
||||
fn gen_from_clap_enum(name: &Ident) -> TokenStream {
|
||||
quote! {
|
||||
fn from_clap(matches: &::structopt::clap::ArgMatches) -> Self {
|
||||
<#name>::from_subcommand(matches.subcommand())
|
||||
|
@ -315,11 +340,11 @@ fn gen_from_clap_enum(name: &Ident) -> quote::Tokens {
|
|||
}
|
||||
}
|
||||
|
||||
fn gen_from_subcommand(name: &Ident, variants: &Punctuated<Variant, Comma>) -> quote::Tokens {
|
||||
fn gen_from_subcommand(name: &Ident, variants: &Punctuated<Variant, Comma>) -> TokenStream {
|
||||
use syn::Fields::*;
|
||||
|
||||
let match_arms = variants.iter().map(|variant| {
|
||||
let attrs = Attrs::from_struct(&variant.attrs, variant.ident.as_ref().to_string());
|
||||
let attrs = Attrs::from_struct(&variant.attrs, variant.ident.to_string());
|
||||
let sub_name = attrs.name();
|
||||
let variant_name = &variant.ident;
|
||||
let constructor_block = match variant.fields {
|
||||
|
@ -329,8 +354,7 @@ fn gen_from_subcommand(name: &Ident, variants: &Punctuated<Variant, Comma>) -> q
|
|||
let ty = &fields.unnamed[0];
|
||||
quote!( ( <#ty as ::structopt::StructOpt>::from_clap(matches) ) )
|
||||
}
|
||||
Unnamed(..) =>
|
||||
panic!("{}: tuple enum are not supported", variant.ident),
|
||||
Unnamed(..) => panic!("{}: tuple enum are not supported", variant.ident),
|
||||
};
|
||||
|
||||
quote! {
|
||||
|
@ -340,7 +364,9 @@ fn gen_from_subcommand(name: &Ident, variants: &Punctuated<Variant, Comma>) -> q
|
|||
});
|
||||
|
||||
quote! {
|
||||
pub fn from_subcommand<'a, 'b>(sub: (&'b str, Option<&'b ::structopt::clap::ArgMatches<'a>>)) -> Option<Self> {
|
||||
pub fn from_subcommand<'a, 'b>(
|
||||
sub: (&'b str, Option<&'b ::structopt::clap::ArgMatches<'a>>)
|
||||
) -> Option<Self> {
|
||||
match sub {
|
||||
#( #match_arms ),*,
|
||||
_ => None
|
||||
|
@ -352,8 +378,8 @@ fn gen_from_subcommand(name: &Ident, variants: &Punctuated<Variant, Comma>) -> q
|
|||
fn impl_structopt_for_struct(
|
||||
name: &Ident,
|
||||
fields: &Punctuated<Field, Comma>,
|
||||
attrs: &[Attribute]
|
||||
) -> quote::Tokens {
|
||||
attrs: &[Attribute],
|
||||
) -> TokenStream {
|
||||
let clap = gen_clap_struct(attrs);
|
||||
let augment_clap = gen_augment_clap(fields);
|
||||
let from_clap = gen_from_clap(name, fields);
|
||||
|
@ -377,8 +403,8 @@ fn impl_structopt_for_struct(
|
|||
fn impl_structopt_for_enum(
|
||||
name: &Ident,
|
||||
variants: &Punctuated<Variant, Comma>,
|
||||
attrs: &[Attribute]
|
||||
) -> quote::Tokens {
|
||||
attrs: &[Attribute],
|
||||
) -> TokenStream {
|
||||
let clap = gen_clap_enum(attrs);
|
||||
let augment_clap = gen_augment_clap_enum(variants);
|
||||
let from_clap = gen_from_clap_enum(name);
|
||||
|
@ -400,16 +426,17 @@ fn impl_structopt_for_enum(
|
|||
}
|
||||
}
|
||||
|
||||
fn impl_structopt(input: &DeriveInput) -> quote::Tokens {
|
||||
fn impl_structopt(input: &DeriveInput) -> TokenStream {
|
||||
use syn::Data::*;
|
||||
|
||||
let struct_name = &input.ident;
|
||||
let inner_impl = match input.data {
|
||||
Struct(DataStruct { fields: syn::Fields::Named(ref fields), .. }) =>
|
||||
impl_structopt_for_struct(struct_name, &fields.named, &input.attrs),
|
||||
Enum(ref e) =>
|
||||
impl_structopt_for_enum(struct_name, &e.variants, &input.attrs),
|
||||
_ => panic!("structopt only supports non-tuple structs and enums")
|
||||
Struct(DataStruct {
|
||||
fields: syn::Fields::Named(ref fields),
|
||||
..
|
||||
}) => impl_structopt_for_struct(struct_name, &fields.named, &input.attrs),
|
||||
Enum(ref e) => impl_structopt_for_enum(struct_name, &e.variants, &input.attrs),
|
||||
_ => panic!("structopt only supports non-tuple structs and enums"),
|
||||
};
|
||||
|
||||
quote!(#inner_impl)
|
||||
|
|
|
@ -20,7 +20,11 @@ fn required_argument() {
|
|||
}
|
||||
assert_eq!(Opt { arg: 42 }, Opt::from_iter(&["test", "42"]));
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test"]).is_err());
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test", "42", "24"]).is_err());
|
||||
assert!(
|
||||
Opt::clap()
|
||||
.get_matches_from_safe(&["test", "42", "24"])
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -31,7 +35,11 @@ fn optional_argument() {
|
|||
}
|
||||
assert_eq!(Opt { arg: Some(42) }, Opt::from_iter(&["test", "42"]));
|
||||
assert_eq!(Opt { arg: None }, Opt::from_iter(&["test"]));
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test", "42", "24"]).is_err());
|
||||
assert!(
|
||||
Opt::clap()
|
||||
.get_matches_from_safe(&["test", "42", "24"])
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -43,7 +51,11 @@ fn argument_with_default() {
|
|||
}
|
||||
assert_eq!(Opt { arg: 24 }, Opt::from_iter(&["test", "24"]));
|
||||
assert_eq!(Opt { arg: 42 }, Opt::from_iter(&["test"]));
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test", "42", "24"]).is_err());
|
||||
assert!(
|
||||
Opt::clap()
|
||||
.get_matches_from_safe(&["test", "42", "24"])
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -55,7 +67,11 @@ fn argument_with_raw_default() {
|
|||
}
|
||||
assert_eq!(Opt { arg: 24 }, Opt::from_iter(&["test", "24"]));
|
||||
assert_eq!(Opt { arg: 42 }, Opt::from_iter(&["test"]));
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test", "42", "24"]).is_err());
|
||||
assert!(
|
||||
Opt::clap()
|
||||
.get_matches_from_safe(&["test", "42", "24"])
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -66,19 +82,27 @@ fn arguments() {
|
|||
}
|
||||
assert_eq!(Opt { arg: vec![24] }, Opt::from_iter(&["test", "24"]));
|
||||
assert_eq!(Opt { arg: vec![] }, Opt::from_iter(&["test"]));
|
||||
assert_eq!(Opt { arg: vec![24, 42] }, Opt::from_iter(&["test", "24", "42"]));
|
||||
assert_eq!(
|
||||
Opt { arg: vec![24, 42] },
|
||||
Opt::from_iter(&["test", "24", "42"])
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn arguments_safe() {
|
||||
#[derive(StructOpt, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
arg: Vec<i32>,
|
||||
}
|
||||
assert_eq!(Opt { arg: vec![24] }, Opt::from_iter_safe(&["test", "24"]).unwrap());
|
||||
assert_eq!(
|
||||
Opt { arg: vec![24] },
|
||||
Opt::from_iter_safe(&["test", "24"]).unwrap()
|
||||
);
|
||||
assert_eq!(Opt { arg: vec![] }, Opt::from_iter_safe(&["test"]).unwrap());
|
||||
assert_eq!(Opt { arg: vec![24, 42] }, Opt::from_iter_safe(&["test", "24", "42"]).unwrap());
|
||||
assert_eq!(
|
||||
Opt { arg: vec![24, 42] },
|
||||
Opt::from_iter_safe(&["test", "24", "42"]).unwrap()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
clap::ErrorKind::ValueValidation,
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#[macro_use] extern crate structopt;
|
||||
#[macro_use]
|
||||
extern crate structopt;
|
||||
|
||||
use structopt::StructOpt;
|
||||
|
||||
|
|
|
@ -11,9 +11,9 @@ extern crate structopt;
|
|||
|
||||
use structopt::StructOpt;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::num::ParseIntError;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::num::ParseIntError;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(StructOpt, PartialEq, Debug)]
|
||||
struct PathOpt {
|
||||
|
@ -48,19 +48,11 @@ fn test_path_opt_simple() {
|
|||
option_path_2: Some(PathBuf::from("j.zip")),
|
||||
},
|
||||
PathOpt::from_clap(&PathOpt::clap().get_matches_from(&[
|
||||
"test",
|
||||
"-p", "/usr/bin",
|
||||
"-v", "/a/b/c",
|
||||
"-v", "/d/e/f",
|
||||
"-v", "/g/h/i",
|
||||
"-q", "j.zip",
|
||||
"test", "-p", "/usr/bin", "-v", "/a/b/c", "-v", "/d/e/f", "-v", "/g/h/i", "-q", "j.zip"
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
fn parse_hex(input: &str) -> Result<u64, ParseIntError> {
|
||||
u64::from_str_radix(input, 16)
|
||||
}
|
||||
|
@ -82,13 +74,12 @@ fn test_parse_hex() {
|
|||
HexOpt::from_clap(&HexOpt::clap().get_matches_from(&["test", "-n", "abcdef"]))
|
||||
);
|
||||
|
||||
let err = HexOpt::clap().get_matches_from_safe(&["test", "-n", "gg"]).unwrap_err();
|
||||
let err = HexOpt::clap()
|
||||
.get_matches_from_safe(&["test", "-n", "gg"])
|
||||
.unwrap_err();
|
||||
assert!(err.message.contains("invalid digit found in string"), err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
fn custom_parser_1(_: &str) -> &'static str {
|
||||
"A"
|
||||
}
|
||||
|
@ -117,14 +108,16 @@ struct NoOpOpt {
|
|||
#[test]
|
||||
fn test_every_custom_parser() {
|
||||
assert_eq!(
|
||||
NoOpOpt { a: "A", b: "B", c: "C", d: "D" },
|
||||
NoOpOpt::from_clap(&NoOpOpt::clap().get_matches_from(&[
|
||||
"test", "-a=?", "-b=?", "-c=?", "-d=?",
|
||||
]))
|
||||
NoOpOpt {
|
||||
a: "A",
|
||||
b: "B",
|
||||
c: "C",
|
||||
d: "D"
|
||||
},
|
||||
NoOpOpt::from_clap(&NoOpOpt::clap().get_matches_from(&["test", "-a=?", "-b=?", "-c=?", "-d=?",]))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Note: can't use `Vec<u8>` directly, as structopt would instead look for
|
||||
// conversion function from `&str` to `u8`.
|
||||
type Bytes = Vec<u8>;
|
||||
|
@ -151,14 +144,16 @@ fn test_parser_with_default_value() {
|
|||
},
|
||||
DefaultedOpt::from_clap(&DefaultedOpt::clap().get_matches_from(&[
|
||||
"test",
|
||||
"-b", "E²=p²c²+m²c⁴",
|
||||
"-i", "9000",
|
||||
"-p", "src/lib.rs",
|
||||
"-b",
|
||||
"E²=p²c²+m²c⁴",
|
||||
"-i",
|
||||
"9000",
|
||||
"-p",
|
||||
"src/lib.rs",
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct Foo(u8);
|
||||
|
||||
|
@ -223,33 +218,72 @@ fn test_custom_bool() {
|
|||
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test"]).is_err());
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test", "-d"]).is_err());
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test", "-dfoo"]).is_err());
|
||||
assert!(
|
||||
Opt::clap()
|
||||
.get_matches_from_safe(&["test", "-dfoo"])
|
||||
.is_err()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { debug: false, verbose: false, tribool: None, bitset: vec![] },
|
||||
Opt {
|
||||
debug: false,
|
||||
verbose: false,
|
||||
tribool: None,
|
||||
bitset: vec![],
|
||||
},
|
||||
Opt::from_iter(&["test", "-dfalse"]),
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { debug: true, verbose: false, tribool: None, bitset: vec![] },
|
||||
Opt {
|
||||
debug: true,
|
||||
verbose: false,
|
||||
tribool: None,
|
||||
bitset: vec![],
|
||||
},
|
||||
Opt::from_iter(&["test", "-dtrue"]),
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { debug: true, verbose: false, tribool: None, bitset: vec![] },
|
||||
Opt {
|
||||
debug: true,
|
||||
verbose: false,
|
||||
tribool: None,
|
||||
bitset: vec![],
|
||||
},
|
||||
Opt::from_iter(&["test", "-dtrue", "-vfalse"]),
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { debug: true, verbose: true, tribool: None, bitset: vec![] },
|
||||
Opt {
|
||||
debug: true,
|
||||
verbose: true,
|
||||
tribool: None,
|
||||
bitset: vec![],
|
||||
},
|
||||
Opt::from_iter(&["test", "-dtrue", "-vtrue"]),
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { debug: true, verbose: false, tribool: Some(false), bitset: vec![] },
|
||||
Opt {
|
||||
debug: true,
|
||||
verbose: false,
|
||||
tribool: Some(false),
|
||||
bitset: vec![],
|
||||
},
|
||||
Opt::from_iter(&["test", "-dtrue", "-tfalse"]),
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { debug: true, verbose: false, tribool: Some(true), bitset: vec![] },
|
||||
Opt {
|
||||
debug: true,
|
||||
verbose: false,
|
||||
tribool: Some(true),
|
||||
bitset: vec![],
|
||||
},
|
||||
Opt::from_iter(&["test", "-dtrue", "-ttrue"]),
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { debug: true, verbose: false, tribool: None, bitset: vec![false, true, false, false] },
|
||||
Opt::from_iter(&["test", "-dtrue", "-bfalse", "-btrue", "-bfalse","-bfalse"]),
|
||||
Opt {
|
||||
debug: true,
|
||||
verbose: false,
|
||||
tribool: None,
|
||||
bitset: vec![false, true, false, false],
|
||||
},
|
||||
Opt::from_iter(&["test", "-dtrue", "-bfalse", "-btrue", "-bfalse", "-bfalse"]),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// except according to those terms.
|
||||
|
||||
#![deny(warnings)]
|
||||
#![cfg(feature = "nightly")]// TODO: remove that when never is stable
|
||||
#![cfg(feature = "nightly")] // TODO: remove that when never is stable
|
||||
#![feature(never_type)]
|
||||
|
||||
#[macro_use]
|
||||
|
@ -26,9 +26,12 @@ fn warning_never_struct() {
|
|||
#[structopt(parse(try_from_str = "try_str"))]
|
||||
s: String,
|
||||
}
|
||||
assert_eq!(Opt { s: "foo".to_string() },
|
||||
Opt::from_iter(&["test", "foo"]));
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
s: "foo".to_string()
|
||||
},
|
||||
Opt::from_iter(&["test", "foo"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -38,10 +41,12 @@ fn warning_never_enum() {
|
|||
Foo {
|
||||
#[structopt(parse(try_from_str = "try_str"))]
|
||||
s: String,
|
||||
}
|
||||
},
|
||||
}
|
||||
assert_eq!(Opt::Foo { s: "foo".to_string() },
|
||||
Opt::from_iter(&["test", "Foo", "foo"]));
|
||||
|
||||
assert_eq!(
|
||||
Opt::Foo {
|
||||
s: "foo".to_string()
|
||||
},
|
||||
Opt::from_iter(&["test", "Foo", "foo"])
|
||||
);
|
||||
}
|
||||
|
||||
|
|
126
tests/flags.rs
126
tests/flags.rs
|
@ -19,16 +19,34 @@ fn unique_flag() {
|
|||
alice: bool,
|
||||
}
|
||||
|
||||
assert_eq!(Opt { alice: false },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test"])));
|
||||
assert_eq!(Opt { alice: true },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"])));
|
||||
assert_eq!(Opt { alice: true },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--alice"])));
|
||||
assert_eq!(
|
||||
Opt { alice: false },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { alice: true },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { alice: true },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--alice"]))
|
||||
);
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test", "-i"]).is_err());
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test", "-a", "foo"]).is_err());
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test", "-a", "-a"]).is_err());
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test", "-a", "--alice"]).is_err());
|
||||
assert!(
|
||||
Opt::clap()
|
||||
.get_matches_from_safe(&["test", "-a", "foo"])
|
||||
.is_err()
|
||||
);
|
||||
assert!(
|
||||
Opt::clap()
|
||||
.get_matches_from_safe(&["test", "-a", "-a"])
|
||||
.is_err()
|
||||
);
|
||||
assert!(
|
||||
Opt::clap()
|
||||
.get_matches_from_safe(&["test", "-a", "--alice"])
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -41,18 +59,32 @@ fn multiple_flag() {
|
|||
bob: u8,
|
||||
}
|
||||
|
||||
assert_eq!(Opt { alice: 0, bob: 0 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test"])));
|
||||
assert_eq!(Opt { alice: 1, bob: 0 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"])));
|
||||
assert_eq!(Opt { alice: 2, bob: 0 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "-a"])));
|
||||
assert_eq!(Opt { alice: 2, bob: 2 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "--alice", "-bb"])));
|
||||
assert_eq!(Opt { alice: 3, bob: 1 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-aaa", "--bob"])));
|
||||
assert_eq!(
|
||||
Opt { alice: 0, bob: 0 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { alice: 1, bob: 0 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { alice: 2, bob: 0 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "-a"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { alice: 2, bob: 2 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "--alice", "-bb"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { alice: 3, bob: 1 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-aaa", "--bob"]))
|
||||
);
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test", "-i"]).is_err());
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test", "-a", "foo"]).is_err());
|
||||
assert!(
|
||||
Opt::clap()
|
||||
.get_matches_from_safe(&["test", "-a", "foo"])
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -65,16 +97,46 @@ fn combined_flags() {
|
|||
bob: u64,
|
||||
}
|
||||
|
||||
assert_eq!(Opt { alice: false, bob: 0 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test"])));
|
||||
assert_eq!(Opt { alice: true, bob: 0 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"])));
|
||||
assert_eq!(Opt { alice: true, bob: 0 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"])));
|
||||
assert_eq!(Opt { alice: false, bob: 1 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-b"])));
|
||||
assert_eq!(Opt { alice: true, bob: 1 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--alice", "--bob"])));
|
||||
assert_eq!(Opt { alice: true, bob: 4 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-bb", "-a", "-bb"])));
|
||||
assert_eq!(
|
||||
Opt {
|
||||
alice: false,
|
||||
bob: 0
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
alice: true,
|
||||
bob: 0
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
alice: true,
|
||||
bob: 0
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
alice: false,
|
||||
bob: 1
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-b"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
alice: true,
|
||||
bob: 1
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--alice", "--bob"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
alice: true,
|
||||
bob: 4
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-bb", "-a", "-bb"]))
|
||||
);
|
||||
}
|
||||
|
|
|
@ -23,9 +23,18 @@ fn flatten() {
|
|||
#[structopt(flatten)]
|
||||
common: Common,
|
||||
}
|
||||
assert_eq!(Opt { common: Common { arg: 42 } }, Opt::from_iter(&["test", "42"]));
|
||||
assert_eq!(
|
||||
Opt {
|
||||
common: Common { arg: 42 }
|
||||
},
|
||||
Opt::from_iter(&["test", "42"])
|
||||
);
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test"]).is_err());
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test", "42", "24"]).is_err());
|
||||
assert!(
|
||||
Opt::clap()
|
||||
.get_matches_from_safe(&["test", "42", "24"])
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -76,8 +85,18 @@ fn flatten_in_subcommand() {
|
|||
Add(Add),
|
||||
}
|
||||
|
||||
assert_eq!(Opt::Fetch { all: false, common: Common { arg: 42 } },
|
||||
Opt::from_iter(&["test", "fetch", "42"]));
|
||||
assert_eq!(Opt::Add(Add { interactive: true, common: Common { arg: 43 } }),
|
||||
Opt::from_iter(&["test", "add", "-i", "43"]));
|
||||
assert_eq!(
|
||||
Opt::Fetch {
|
||||
all: false,
|
||||
common: Common { arg: 42 }
|
||||
},
|
||||
Opt::from_iter(&["test", "fetch", "42"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt::Add(Add {
|
||||
interactive: true,
|
||||
common: Common { arg: 43 }
|
||||
}),
|
||||
Opt::from_iter(&["test", "add", "-i", "43"])
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#[macro_use] extern crate structopt;
|
||||
#[macro_use]
|
||||
extern crate structopt;
|
||||
|
||||
use structopt::StructOpt;
|
||||
|
||||
|
@ -17,7 +18,7 @@ struct Opt {
|
|||
#[structopt(short = "v", long = "verbose", parse(from_occurrences))]
|
||||
verbose: u64,
|
||||
#[structopt(subcommand)]
|
||||
cmd: Sub
|
||||
cmd: Sub,
|
||||
}
|
||||
|
||||
#[derive(StructOpt, PartialEq, Debug)]
|
||||
|
@ -25,7 +26,7 @@ enum Sub {
|
|||
#[structopt(name = "fetch")]
|
||||
Fetch {},
|
||||
#[structopt(name = "add")]
|
||||
Add {}
|
||||
Add {},
|
||||
}
|
||||
|
||||
#[derive(StructOpt, PartialEq, Debug)]
|
||||
|
@ -35,7 +36,7 @@ struct Opt2 {
|
|||
#[structopt(short = "v", long = "verbose", parse(from_occurrences))]
|
||||
verbose: u64,
|
||||
#[structopt(subcommand)]
|
||||
cmd: Option<Sub>
|
||||
cmd: Option<Sub>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -43,24 +44,54 @@ fn test_no_cmd() {
|
|||
let result = Opt::clap().get_matches_from_safe(&["test"]);
|
||||
assert!(result.is_err());
|
||||
|
||||
assert_eq!(Opt2 { force: false, verbose: 0, cmd: None },
|
||||
Opt2::from_clap(&Opt2::clap().get_matches_from(&["test"])));
|
||||
assert_eq!(
|
||||
Opt2 {
|
||||
force: false,
|
||||
verbose: 0,
|
||||
cmd: None
|
||||
},
|
||||
Opt2::from_clap(&Opt2::clap().get_matches_from(&["test"]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fetch() {
|
||||
assert_eq!(Opt { force: false, verbose: 3, cmd: Sub::Fetch {} },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-vvv", "fetch"])));
|
||||
assert_eq!(Opt { force: true, verbose: 0, cmd: Sub::Fetch {} },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--force", "fetch"])));
|
||||
assert_eq!(
|
||||
Opt {
|
||||
force: false,
|
||||
verbose: 3,
|
||||
cmd: Sub::Fetch {}
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-vvv", "fetch"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
force: true,
|
||||
verbose: 0,
|
||||
cmd: Sub::Fetch {}
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--force", "fetch"]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
assert_eq!(Opt { force: false, verbose: 0, cmd: Sub::Add {} },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "add"])));
|
||||
assert_eq!(Opt { force: false, verbose: 2, cmd: Sub::Add {} },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-vv", "add"])));
|
||||
assert_eq!(
|
||||
Opt {
|
||||
force: false,
|
||||
verbose: 0,
|
||||
cmd: Sub::Add {}
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "add"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
force: false,
|
||||
verbose: 2,
|
||||
cmd: Sub::Add {}
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-vv", "add"]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -80,7 +111,7 @@ struct Opt3 {
|
|||
#[structopt(short = "a", long = "all")]
|
||||
all: bool,
|
||||
#[structopt(subcommand)]
|
||||
cmd: Sub2
|
||||
cmd: Sub2,
|
||||
}
|
||||
|
||||
#[derive(StructOpt, PartialEq, Debug)]
|
||||
|
@ -89,11 +120,10 @@ enum Sub2 {
|
|||
Foo {
|
||||
file: String,
|
||||
#[structopt(subcommand)]
|
||||
cmd: Sub3
|
||||
cmd: Sub3,
|
||||
},
|
||||
#[structopt(name = "bar")]
|
||||
Bar {
|
||||
}
|
||||
Bar {},
|
||||
}
|
||||
|
||||
#[derive(StructOpt, PartialEq, Debug)]
|
||||
|
@ -101,7 +131,7 @@ enum Sub3 {
|
|||
#[structopt(name = "baz")]
|
||||
Baz {},
|
||||
#[structopt(name = "quux")]
|
||||
Quux {}
|
||||
Quux {},
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -109,7 +139,10 @@ fn test_subsubcommand() {
|
|||
assert_eq!(
|
||||
Opt3 {
|
||||
all: true,
|
||||
cmd: Sub2::Foo { file: "lib.rs".to_string(), cmd: Sub3::Quux {} }
|
||||
cmd: Sub2::Foo {
|
||||
file: "lib.rs".to_string(),
|
||||
cmd: Sub3::Quux {}
|
||||
}
|
||||
},
|
||||
Opt3::from_clap(&Opt3::clap().get_matches_from(&["test", "--all", "foo", "lib.rs", "quux"]))
|
||||
);
|
||||
|
@ -117,43 +150,57 @@ fn test_subsubcommand() {
|
|||
|
||||
#[derive(StructOpt, PartialEq, Debug)]
|
||||
enum SubSubCmdWithOption {
|
||||
#[structopt(name = "remote")]
|
||||
#[structopt(name = "remote")]
|
||||
Remote {
|
||||
#[structopt(subcommand)]
|
||||
cmd: Option<Remote>
|
||||
cmd: Option<Remote>,
|
||||
},
|
||||
#[structopt(name = "stash")]
|
||||
Stash {
|
||||
#[structopt(subcommand)]
|
||||
cmd: Stash
|
||||
cmd: Stash,
|
||||
},
|
||||
}
|
||||
#[derive(StructOpt, PartialEq, Debug)]
|
||||
enum Remote {
|
||||
#[structopt(name = "add")]
|
||||
#[structopt(name = "add")]
|
||||
Add { name: String, url: String },
|
||||
#[structopt(name = "remove")]
|
||||
#[structopt(name = "remove")]
|
||||
Remove { name: String },
|
||||
}
|
||||
|
||||
#[derive(StructOpt, PartialEq, Debug)]
|
||||
enum Stash {
|
||||
#[structopt(name = "save")]
|
||||
#[structopt(name = "save")]
|
||||
Save,
|
||||
#[structopt(name = "pop")]
|
||||
#[structopt(name = "pop")]
|
||||
Pop,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sub_sub_cmd_with_option() {
|
||||
fn make(args: &[&str]) -> Option<SubSubCmdWithOption> {
|
||||
SubSubCmdWithOption::clap().get_matches_from_safe(args).ok().map(|m| SubSubCmdWithOption::from_clap(&m))
|
||||
SubSubCmdWithOption::clap()
|
||||
.get_matches_from_safe(args)
|
||||
.ok()
|
||||
.map(|m| SubSubCmdWithOption::from_clap(&m))
|
||||
}
|
||||
assert_eq!(Some(SubSubCmdWithOption::Remote { cmd: None }), make(&["", "remote"]));
|
||||
assert_eq!(
|
||||
Some(SubSubCmdWithOption::Remote { cmd: Some(Remote::Add { name: "origin".into(), url: "http".into() }) }),
|
||||
Some(SubSubCmdWithOption::Remote { cmd: None }),
|
||||
make(&["", "remote"])
|
||||
);
|
||||
assert_eq!(
|
||||
Some(SubSubCmdWithOption::Remote {
|
||||
cmd: Some(Remote::Add {
|
||||
name: "origin".into(),
|
||||
url: "http".into()
|
||||
})
|
||||
}),
|
||||
make(&["", "remote", "add", "origin", "http"])
|
||||
);
|
||||
assert_eq!(Some(SubSubCmdWithOption::Stash { cmd: Stash::Save }), make(&["", "stash", "save"]));
|
||||
assert_eq!(
|
||||
Some(SubSubCmdWithOption::Stash { cmd: Stash::Save }),
|
||||
make(&["", "stash", "save"])
|
||||
);
|
||||
assert_eq!(None, make(&["", "stash"]));
|
||||
}
|
||||
|
|
101
tests/options.rs
101
tests/options.rs
|
@ -18,14 +18,24 @@ fn required_option() {
|
|||
#[structopt(short = "a", long = "arg")]
|
||||
arg: i32,
|
||||
}
|
||||
assert_eq!(Opt { arg: 42 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a42"])));
|
||||
assert_eq!(Opt { arg: 42 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "42"])));
|
||||
assert_eq!(Opt { arg: 42 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--arg", "42"])));
|
||||
assert_eq!(
|
||||
Opt { arg: 42 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a42"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { arg: 42 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "42"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { arg: 42 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--arg", "42"]))
|
||||
);
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test"]).is_err());
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test", "-a42", "-a24"]).is_err());
|
||||
assert!(
|
||||
Opt::clap()
|
||||
.get_matches_from_safe(&["test", "-a42", "-a24"])
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -35,11 +45,19 @@ fn optional_option() {
|
|||
#[structopt(short = "a")]
|
||||
arg: Option<i32>,
|
||||
}
|
||||
assert_eq!(Opt { arg: Some(42) },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a42"])));
|
||||
assert_eq!(Opt { arg: None },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test"])));
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test", "-a42", "-a24"]).is_err());
|
||||
assert_eq!(
|
||||
Opt { arg: Some(42) },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a42"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { arg: None },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test"]))
|
||||
);
|
||||
assert!(
|
||||
Opt::clap()
|
||||
.get_matches_from_safe(&["test", "-a42", "-a24"])
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -49,11 +67,19 @@ fn option_with_default() {
|
|||
#[structopt(short = "a", default_value = "42")]
|
||||
arg: i32,
|
||||
}
|
||||
assert_eq!(Opt { arg: 24 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a24"])));
|
||||
assert_eq!(Opt { arg: 42 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test"])));
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test", "-a42", "-a24"]).is_err());
|
||||
assert_eq!(
|
||||
Opt { arg: 24 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a24"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { arg: 42 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test"]))
|
||||
);
|
||||
assert!(
|
||||
Opt::clap()
|
||||
.get_matches_from_safe(&["test", "-a42", "-a24"])
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -63,11 +89,19 @@ fn option_with_raw_default() {
|
|||
#[structopt(short = "a", raw(default_value = r#""42""#))]
|
||||
arg: i32,
|
||||
}
|
||||
assert_eq!(Opt { arg: 24 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a24"])));
|
||||
assert_eq!(Opt { arg: 42 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test"])));
|
||||
assert!(Opt::clap().get_matches_from_safe(&["test", "-a42", "-a24"]).is_err());
|
||||
assert_eq!(
|
||||
Opt { arg: 24 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a24"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { arg: 42 },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test"]))
|
||||
);
|
||||
assert!(
|
||||
Opt::clap()
|
||||
.get_matches_from_safe(&["test", "-a42", "-a24"])
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -77,12 +111,18 @@ fn options() {
|
|||
#[structopt(short = "a", long = "arg")]
|
||||
arg: Vec<i32>,
|
||||
}
|
||||
assert_eq!(Opt { arg: vec![24] },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a24"])));
|
||||
assert_eq!(Opt { arg: vec![] },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test"])));
|
||||
assert_eq!(Opt { arg: vec![24, 42] },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a24", "--arg", "42"])));
|
||||
assert_eq!(
|
||||
Opt { arg: vec![24] },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a24"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { arg: vec![] },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { arg: vec![24, 42] },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a24", "--arg", "42"]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -93,5 +133,8 @@ fn empy_default_value() {
|
|||
arg: String,
|
||||
}
|
||||
assert_eq!(Opt { arg: "".into() }, Opt::from_iter(&["test"]));
|
||||
assert_eq!(Opt { arg: "foo".into() }, Opt::from_iter(&["test", "-afoo"]));
|
||||
assert_eq!(
|
||||
Opt { arg: "foo".into() },
|
||||
Opt::from_iter(&["test", "-afoo"])
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,18 +12,18 @@ extern crate structopt;
|
|||
mod options {
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct Options {
|
||||
#[structopt(subcommand)]
|
||||
pub subcommand: ::subcommands::SubCommand,
|
||||
#[structopt(subcommand)]
|
||||
pub subcommand: ::subcommands::SubCommand,
|
||||
}
|
||||
}
|
||||
|
||||
mod subcommands {
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub enum SubCommand {
|
||||
#[structopt(name = "foo", about = "foo")]
|
||||
Foo {
|
||||
#[structopt(help = "foo")]
|
||||
bars: Vec<String>,
|
||||
},
|
||||
#[structopt(name = "foo", about = "foo")]
|
||||
Foo {
|
||||
#[structopt(help = "foo")]
|
||||
bars: Vec<String>,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@ use structopt::clap::AppSettings;
|
|||
#[structopt(raw(global_settings = "&[AppSettings::ColoredHelp]"))]
|
||||
struct Opt {
|
||||
#[structopt(long = "x",
|
||||
raw(display_order = "2", next_line_help = "true",
|
||||
default_value = r#""0""#, require_equals = "true"))]
|
||||
raw(display_order = "2", next_line_help = "true", default_value = r#""0""#,
|
||||
require_equals = "true"))]
|
||||
x: i32,
|
||||
|
||||
#[structopt(short = "l", long = "level", raw(aliases = r#"&["set-level", "lvl"]"#))]
|
||||
|
@ -33,22 +33,64 @@ struct Opt {
|
|||
|
||||
#[test]
|
||||
fn test_raw_slice() {
|
||||
assert_eq!(Opt { x: 0, level: "1".to_string(), files: Vec::new(), values: vec![] },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-l", "1"])));
|
||||
assert_eq!(Opt { x: 0, level: "1".to_string(), files: Vec::new(), values: vec![] },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--level", "1"])));
|
||||
assert_eq!(Opt { x: 0, level: "1".to_string(), files: Vec::new(), values: vec![] },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--set-level", "1"])));
|
||||
assert_eq!(Opt { x: 0, level: "1".to_string(), files: Vec::new(), values: vec![] },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--lvl", "1"])));
|
||||
assert_eq!(
|
||||
Opt {
|
||||
x: 0,
|
||||
level: "1".to_string(),
|
||||
files: Vec::new(),
|
||||
values: vec![],
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-l", "1"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
x: 0,
|
||||
level: "1".to_string(),
|
||||
files: Vec::new(),
|
||||
values: vec![],
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--level", "1"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
x: 0,
|
||||
level: "1".to_string(),
|
||||
files: Vec::new(),
|
||||
values: vec![],
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--set-level", "1"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
x: 0,
|
||||
level: "1".to_string(),
|
||||
files: Vec::new(),
|
||||
values: vec![],
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--lvl", "1"]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_raw_multi_args() {
|
||||
assert_eq!(Opt { x: 0, level: "1".to_string(), files: vec!["file".to_string()], values: vec![] },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-l", "1", "file"])));
|
||||
assert_eq!(Opt { x: 0, level: "1".to_string(), files: vec!["FILE".to_string()], values: vec![1] },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-l", "1", "--values", "1", "--", "FILE"])));
|
||||
assert_eq!(
|
||||
Opt {
|
||||
x: 0,
|
||||
level: "1".to_string(),
|
||||
files: vec!["file".to_string()],
|
||||
values: vec![],
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-l", "1", "file"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
x: 0,
|
||||
level: "1".to_string(),
|
||||
files: vec!["FILE".to_string()],
|
||||
values: vec![1],
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-l", "1", "--values", "1", "--", "FILE"]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -59,8 +101,15 @@ fn test_raw_multi_args_fail() {
|
|||
|
||||
#[test]
|
||||
fn test_raw_bool() {
|
||||
assert_eq!(Opt { x: 1, level: "1".to_string(), files: vec![], values: vec![] },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-l", "1", "--x=1"])));
|
||||
assert_eq!(
|
||||
Opt {
|
||||
x: 1,
|
||||
level: "1".to_string(),
|
||||
files: vec![],
|
||||
values: vec![],
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-l", "1", "--x=1"]))
|
||||
);
|
||||
let result = Opt::clap().get_matches_from_safe(&["test", "-l", "1", "--x", "1"]);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#[macro_use] extern crate structopt;
|
||||
#[macro_use]
|
||||
extern crate structopt;
|
||||
|
||||
use structopt::StructOpt;
|
||||
|
||||
|
@ -19,32 +20,54 @@ enum Opt {
|
|||
#[structopt(short = "f", long = "force")]
|
||||
/// Overwrite local branches.
|
||||
force: bool,
|
||||
repo: String
|
||||
repo: String,
|
||||
},
|
||||
|
||||
|
||||
#[structopt(name = "add")]
|
||||
Add {
|
||||
#[structopt(short = "i", long = "interactive")]
|
||||
interactive: bool,
|
||||
#[structopt(short = "v", long = "verbose")]
|
||||
verbose: bool
|
||||
}
|
||||
verbose: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fetch() {
|
||||
assert_eq!(Opt::Fetch { all: true, force: false, repo: "origin".to_string() },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "fetch", "--all", "origin"])));
|
||||
assert_eq!(Opt::Fetch { all: false, force: true, repo: "origin".to_string() },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "fetch", "-f", "origin"])));
|
||||
assert_eq!(
|
||||
Opt::Fetch {
|
||||
all: true,
|
||||
force: false,
|
||||
repo: "origin".to_string()
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "fetch", "--all", "origin"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt::Fetch {
|
||||
all: false,
|
||||
force: true,
|
||||
repo: "origin".to_string()
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "fetch", "-f", "origin"]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
assert_eq!(Opt::Add { interactive: false, verbose: false },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "add"])));
|
||||
assert_eq!(Opt::Add { interactive: true, verbose: true },
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "add", "-i", "-v"])));
|
||||
assert_eq!(
|
||||
Opt::Add {
|
||||
interactive: false,
|
||||
verbose: false
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "add"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt::Add {
|
||||
interactive: true,
|
||||
verbose: true
|
||||
},
|
||||
Opt::from_clap(&Opt::clap().get_matches_from(&["test", "add", "-i", "-v"]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -62,17 +85,19 @@ fn test_no_parse() {
|
|||
#[derive(StructOpt, PartialEq, Debug)]
|
||||
enum Opt2 {
|
||||
#[structopt(name = "do-something")]
|
||||
DoSomething {
|
||||
arg: String
|
||||
}
|
||||
DoSomething { arg: String },
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// This test is specifically to make sure that hyphenated subcommands get
|
||||
/// processed correctly.
|
||||
fn test_hyphenated_subcommands() {
|
||||
assert_eq!(Opt2::DoSomething { arg: "blah".to_string() },
|
||||
Opt2::from_clap(&Opt2::clap().get_matches_from(&["test", "do-something", "blah"])));
|
||||
assert_eq!(
|
||||
Opt2::DoSomething {
|
||||
arg: "blah".to_string()
|
||||
},
|
||||
Opt2::from_clap(&Opt2::clap().get_matches_from(&["test", "do-something", "blah"]))
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(StructOpt, PartialEq, Debug)]
|
||||
|
@ -82,14 +107,23 @@ enum Opt3 {
|
|||
#[structopt(name = "init")]
|
||||
Init,
|
||||
#[structopt(name = "fetch")]
|
||||
Fetch
|
||||
Fetch,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_commands() {
|
||||
assert_eq!(Opt3::Add, Opt3::from_clap(&Opt3::clap().get_matches_from(&["test", "add"])));
|
||||
assert_eq!(Opt3::Init, Opt3::from_clap(&Opt3::clap().get_matches_from(&["test", "init"])));
|
||||
assert_eq!(Opt3::Fetch, Opt3::from_clap(&Opt3::clap().get_matches_from(&["test", "fetch"])));
|
||||
assert_eq!(
|
||||
Opt3::Add,
|
||||
Opt3::from_clap(&Opt3::clap().get_matches_from(&["test", "add"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt3::Init,
|
||||
Opt3::from_clap(&Opt3::clap().get_matches_from(&["test", "init"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt3::Fetch,
|
||||
Opt3::from_clap(&Opt3::clap().get_matches_from(&["test", "fetch"]))
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(StructOpt, PartialEq, Debug)]
|
||||
|
@ -117,12 +151,19 @@ enum Opt4 {
|
|||
#[test]
|
||||
fn test_tuple_commands() {
|
||||
assert_eq!(
|
||||
Opt4::Add(Add { file: "f".to_string() }),
|
||||
Opt4::Add(Add {
|
||||
file: "f".to_string()
|
||||
}),
|
||||
Opt4::from_clap(&Opt4::clap().get_matches_from(&["test", "add", "f"]))
|
||||
);
|
||||
assert_eq!(Opt4::Init, Opt4::from_clap(&Opt4::clap().get_matches_from(&["test", "init"])));
|
||||
assert_eq!(
|
||||
Opt4::Fetch(Fetch { remote: "origin".to_string() }),
|
||||
Opt4::Init,
|
||||
Opt4::from_clap(&Opt4::clap().get_matches_from(&["test", "init"]))
|
||||
);
|
||||
assert_eq!(
|
||||
Opt4::Fetch(Fetch {
|
||||
remote: "origin".to_string()
|
||||
}),
|
||||
Opt4::from_clap(&Opt4::clap().get_matches_from(&["test", "fetch", "origin"]))
|
||||
);
|
||||
|
||||
|
@ -140,7 +181,7 @@ fn enum_in_enum_subsubcommand() {
|
|||
#[derive(StructOpt, Debug, PartialEq)]
|
||||
pub enum Opt {
|
||||
#[structopt(name = "daemon")]
|
||||
Daemon(DaemonCommand)
|
||||
Daemon(DaemonCommand),
|
||||
}
|
||||
|
||||
#[derive(StructOpt, Debug, PartialEq)]
|
||||
|
@ -175,5 +216,10 @@ fn flatten_enum() {
|
|||
}
|
||||
|
||||
assert!(Opt::from_iter_safe(&["test"]).is_err());
|
||||
assert_eq!(Opt::from_iter(&["test", "Foo"]), Opt { sub_cmd: SubCmd::Foo });
|
||||
assert_eq!(
|
||||
Opt::from_iter(&["test", "Foo"]),
|
||||
Opt {
|
||||
sub_cmd: SubCmd::Foo
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue