From bcc6a89b495c46139c7c356acb74dd4334021a84 Mon Sep 17 00:00:00 2001 From: Wim Looman Date: Mon, 30 Jan 2017 18:31:20 +0100 Subject: [PATCH 1/8] Quick port of stomp PoC into clap --- Cargo.toml | 3 +- clap-macros/Cargo.toml | 11 ++ clap-macros/src/attr.rs | 117 +++++++++++++ clap-macros/src/attrs.rs | 153 +++++++++++++++++ clap-macros/src/define_app.rs | 140 ++++++++++++++++ clap-macros/src/define_sub_commands.rs | 46 ++++++ clap-macros/src/field.rs | 156 ++++++++++++++++++ clap-macros/src/from_arg_matches.rs | 123 ++++++++++++++ clap-macros/src/lib.rs | 47 ++++++ .../src/sub_command_from_arg_matches.rs | 56 +++++++ examples/01b_quick_example_stomp.rs | 106 ++++++++++++ src/lib.rs | 2 + src/stomp.rs | 49 ++++++ 13 files changed, 1008 insertions(+), 1 deletion(-) create mode 100644 clap-macros/Cargo.toml create mode 100644 clap-macros/src/attr.rs create mode 100644 clap-macros/src/attrs.rs create mode 100644 clap-macros/src/define_app.rs create mode 100644 clap-macros/src/define_sub_commands.rs create mode 100644 clap-macros/src/field.rs create mode 100644 clap-macros/src/from_arg_matches.rs create mode 100644 clap-macros/src/lib.rs create mode 100644 clap-macros/src/sub_command_from_arg_matches.rs create mode 100644 examples/01b_quick_example_stomp.rs create mode 100644 src/stomp.rs diff --git a/Cargo.toml b/Cargo.toml index bce15373793..deca7a18bdb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,8 @@ yaml-rust = { version = "0.3.5", optional = true } clippy = { version = "~0.0.112", optional = true } [dev-dependencies] -regex = "~0.1.80" +regex = "~0.1.69" +clap-macros = { path = "clap-macros" } [features] default = ["suggestions", "color", "wrap_help"] diff --git a/clap-macros/Cargo.toml b/clap-macros/Cargo.toml new file mode 100644 index 00000000000..99845e8f4e3 --- /dev/null +++ b/clap-macros/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "clap-macros" +version = "0.1.0" +authors = ["Wim Looman "] + +[lib] +proc-macro = true + +[dependencies] +syn = "0.11" +quote = "0.3" diff --git a/clap-macros/src/attr.rs b/clap-macros/src/attr.rs new file mode 100644 index 00000000000..032935e37dd --- /dev/null +++ b/clap-macros/src/attr.rs @@ -0,0 +1,117 @@ +use syn; +use quote; + +pub struct Attribute { + key: String, + values: Vec, +} + +impl Attribute { + pub fn new(key: String) -> Attribute { + Attribute { key: key, values: vec![] } + } + + pub fn push(&mut self, value: syn::Lit) { + self.values.push(value) + } + + pub fn values(&self) -> Vec { + self.values.iter().map(|s| match *s { + syn::Lit::Str(ref s, _) => s.clone(), + _ => panic!("clap-macros: multi-valued attributes must be strings"), + }).collect() + } + + fn only_value(&self) -> &syn::Lit { + if self.values.len() == 1 { + &self.values[0] + } else { + panic!("clap-macros: expected a single value for attribute '{}' but had multiple", self.key); + } + } +} + +impl<'a> Into for &'a Attribute { + fn into(self) -> syn::Lit { + self.only_value().clone() + } +} + +impl<'a> Into<&'a syn::Lit> for &'a Attribute { + fn into(self) -> &'a syn::Lit { + self.only_value() + } +} + +impl<'a> Into<&'a str> for &'a Attribute { + fn into(self) -> &'a str { + if let &syn::Lit::Str(ref value, _) = self.only_value() { + value + } else { + panic!("Expected string value for attribute {} but got a {:?}", self.key, self.only_value()); + } + } +} + +impl<'a> Into<&'a [u8]> for &'a Attribute { + fn into(self) -> &'a [u8] { + if let &syn::Lit::ByteStr(ref value, _) = self.only_value() { + value + } else { + panic!("Expected bytestring value for attribute {} but got a {:?}", self.key, self.only_value()); + } + } +} + +impl<'a> Into for &'a Attribute { + fn into(self) -> u8 { + if let &syn::Lit::Byte(ref value) = self.only_value() { + *value + } else { + panic!("Expected byte value for attribute {} but got a {:?}", self.key, self.only_value()); + } + } +} + +impl<'a> Into for &'a Attribute { + fn into(self) -> char { + if let &syn::Lit::Char(ref value) = self.only_value() { + *value + } else { + panic!("Expected char value for attribute {} but got a {:?}", self.key, self.only_value()); + } + } +} + +impl<'a> Into for &'a Attribute { + fn into(self) -> u64 { + if let &syn::Lit::Int(ref value, _) = self.only_value() { + *value + } else { + panic!("Expected int value for attribute {} but got a {:?}", self.key, self.only_value()); + } + } +} + +impl<'a> Into for &'a Attribute { + fn into(self) -> bool { + if let &syn::Lit::Bool(ref value) = self.only_value() { + *value + } else { + panic!("Expected bool value for attribute {} but got a {:?}", self.key, self.only_value()); + } + } +} + +impl<'a> quote::ToTokens for &'a mut Attribute { + fn to_tokens(&self, tokens: &mut quote::Tokens) { + self.only_value().to_tokens(tokens) + } +} + +impl quote::ToTokens for Attribute { + fn to_tokens(&self, tokens: &mut quote::Tokens) { + self.only_value().to_tokens(tokens) + } +} + diff --git a/clap-macros/src/attrs.rs b/clap-macros/src/attrs.rs new file mode 100644 index 00000000000..cd125f00a5c --- /dev/null +++ b/clap-macros/src/attrs.rs @@ -0,0 +1,153 @@ +use std::cell::RefCell; +use std::collections::{ BTreeMap, HashMap }; + +use syn; + +use attr::Attribute; + +pub struct Attributes { + pub summary: String, + pub docs: String, + map: BTreeMap, Attribute)>, +} + +pub struct FieldAttributes { + empty: Attributes, + map: HashMap, Attributes)>, +} + +impl Attributes { + pub fn check_used(&self, name: &str, field: Option<&str>) { + for (ref attr, &(ref counter, _)) in &self.map { + if *counter.borrow() == 0 { + match field { + Some(field) => + println!("clap-macros: unexpected attribute '{}' on field '{}' of struct '{}'", attr, field, name), + None => + println!("clap-macros: unexpected attribute '{}' on struct '{}'", attr, name), + } + } + } + } + + pub fn get(&self, key: &str) -> Option<&Attribute> { + if let Some(&(ref counter, ref attr)) = self.map.get(key) { + *counter.borrow_mut() += 1; + Some(attr) + } else { + None + } + } + + pub fn get_bool(&self, key: &str) -> bool { + self.get(key).map(|a| a.into()).unwrap_or(false) + } +} + +impl FieldAttributes { + pub fn check_used(&self, name: &str) { + for (ref field, &(ref counter, ref attrs)) in &self.map { + if *counter.borrow() == 0 { + panic!("clap-macros: didn't access attributes for field '{}' on struct '{}' for some reason", field, name); + } + attrs.check_used(name, Some(field.as_ref())); + } + } + + pub fn get(&self, field: &syn::Field) -> &Attributes { + if let Some(&(ref counter, ref attrs)) = self.map.get(field.ident.as_ref().unwrap()) { + *counter.borrow_mut() += 1; + attrs + } else { + &self.empty + } + } +} + +fn extract_attrs_inner(attrs: &Vec) -> Attributes { + let mut claps = BTreeMap::new(); + for attr in attrs { + if let syn::MetaItem::List(ref ident, ref values) = attr.value { + if ident == "clap" { + for value in values { + match *value { + syn::NestedMetaItem::MetaItem(ref item) => match *item { + syn::MetaItem::NameValue(ref name, ref value) => { + let &mut (_, ref mut attr) = claps.entry(name.to_string()).or_insert((RefCell::new(0), Attribute::new(name.to_string()))); + attr.push(value.clone()); + } + syn::MetaItem::Word(ref name) => { + let &mut (_, ref mut attr) = claps.entry(name.to_string()).or_insert((RefCell::new(0), Attribute::new(name.to_string()))); + attr.push(syn::Lit::Bool(true)); + } + syn::MetaItem::List(ref ident, ref values) => { + let &mut (_, ref mut attr) = claps.entry(ident.as_ref().to_string()).or_insert((RefCell::new(0), Attribute::new(ident.as_ref().to_string()))); + for value in values { + match *value { + syn::NestedMetaItem::MetaItem(ref item) => match *item { + syn::MetaItem::Word(ref name) => { + attr.push(name.as_ref().into()); + } + syn::MetaItem::NameValue(..) => { + panic!("Invalid clap attribute {} named value in sublist not supported", quote!(#attr).to_string().replace(" ", "")); + } + syn::MetaItem::List(..) => { + panic!("Invalid clap attribute {} sublist in sublist not supported", quote!(#attr).to_string().replace(" ", "")); + } + }, + syn::NestedMetaItem::Literal(_) => { + panic!("Invalid clap attribute {} literal value not supported", quote!(#attr).to_string().replace(" ", "")); + }, + } + } + } + }, + syn::NestedMetaItem::Literal(_) => { + panic!("Invalid clap attribute {} literal value not supported", quote!(#attr).to_string().replace(" ", "")); + }, + } + } + } + } + } + + let docs = attrs.iter() + .filter(|a| a.is_sugared_doc) + .map(|a| match a.value { + syn::MetaItem::NameValue(_, syn::Lit::Str(ref doc, _)) => doc, + _ => unreachable!(), + }) + .fold(String::new(), |docs, line| docs + line.trim_left_matches('/').trim() + "\n"); + + let index = docs.find("\n\n"); + let (summary, docs) = if let Some(index) = index { + let (summary, docs) = docs.split_at(index); + let (_, docs) = docs.split_at(2); + (summary.into(), docs.into()) + } else { + (docs, "".into()) + }; + + Attributes { summary: summary, docs: docs, map: claps } +} + +/// Extracts all clap attributes of the form #[clap(i = V)] +pub fn extract_attrs(ast: &syn::MacroInput) -> (Attributes, FieldAttributes) { + let empty = Attributes { summary: "".into(), docs: "".into(), map: BTreeMap::new() }; + let root_attrs = extract_attrs_inner(&ast.attrs); + let field_attrs = match ast.body { + syn::Body::Struct(syn::VariantData::Struct(ref fields)) => { + fields + .iter() + .map(|field| (field.ident.clone().unwrap(), (RefCell::new(0), extract_attrs_inner(&field.attrs)))) + .collect() + } + syn::Body::Struct(syn::VariantData::Tuple(_)) => { + panic!("TODO: tuple struct unsupported msg") + } + syn::Body::Struct(syn::VariantData::Unit) | syn::Body::Enum(_) => { + HashMap::new() + } + }; + (root_attrs, FieldAttributes { empty: empty, map: field_attrs }) +} diff --git a/clap-macros/src/define_app.rs b/clap-macros/src/define_app.rs new file mode 100644 index 00000000000..23168841edb --- /dev/null +++ b/clap-macros/src/define_app.rs @@ -0,0 +1,140 @@ +use syn; +use quote; + +use attrs::{ Attributes, FieldAttributes }; +use field::{ Arg, Field, Subcommand }; + +fn expand_arg(arg: &Arg) -> quote::Tokens { + let name = arg.name; + let ty = arg.ty; + let short = arg.short.as_ref().map(|s| quote! { .short(#s) }); + let long = arg.long.map(|s| quote! { .long(#s) }); + let value_name = arg.value_name.map(|s| quote! { .value_name(#s) }); + let takes_value = arg.takes_value; + let index = arg.index.map(|i| quote! { .index(#i) }); + let docs = (arg.summary.to_string() + "\n\n" + arg.docs).trim().to_string(); + let multiple = arg.multiple; + let default_value = arg.default_value.map(|d| quote! { .default_value(#d) }); + let min_values = arg.min_values.map(|m| quote! { .min_values(#m) }); + let max_values = arg.max_values.map(|m| quote! { .max_values(#m) }); + let required = arg.required; + let validator = if arg.takes_value { + Some(quote! { + .validator(|s| { + <#ty as ::std::str::FromStr>::from_str(&s) + .map(|_| ()) + .map_err(|e| format!("failed to parse value {:?} for argument '{}': {}", s, #name, e)) + }) + }) + } else { + None + }; + + quote! { + ::clap::Arg::with_name(#name) + #short + #long + #value_name + #index + .help(#docs) + .takes_value(#takes_value) + .multiple(#multiple) + #default_value + #min_values + #max_values + .required(#required) + #validator + } +} + +fn expand_args<'a, 'b: 'a, I>(args: I) -> quote::Tokens where I: Iterator> { + let args = args.map(expand_arg); + quote! { .args(&[#(#args),*]) } +} + +fn expand_subcommand(subcommand: &Subcommand) -> quote::Tokens { + let ty = subcommand.ty; + let required = if subcommand.is_optional { + None + } else { + Some(quote! { .setting(::clap::AppSettings::SubcommandRequiredElseHelp) }) + }; + + quote! { + .subcommands(<#ty as ::clap::stomp::DefineSubCommands>::subcommands()) + #required + } +} + +fn expand_app(ast: &syn::MacroInput, attrs: &Attributes, fields: &[Field]) -> quote::Tokens { + let name = attrs.get("name").map(|a| a.into()) + .unwrap_or_else(|| syn::Lit::from(ast.ident.as_ref().to_lowercase())); + + let version = if attrs.get_bool("crate_version") { + Some(quote! { .version(crate_version!()) }) + } else { + attrs.get("version").map(|a| quote! { .version(#a) }) + }; + + let author = if attrs.get_bool("crate_authors") { + Some(quote! { .author(crate_authors!()) }) + } else { + attrs.get("author").map(|a| quote! { .author(#a) }) + }; + + let args = expand_args(fields.iter().filter_map(|field| field.arg())); + let subcommand = fields.iter() + .filter_map(|field| field.subcommand()) + .find(|_| true) + .map(expand_subcommand); + + let ref summary = attrs.summary; + let ref docs = attrs.docs; + let alias = attrs.get("alias").map(|a| quote! { .alias(#a) }); + let global_settings = attrs.get("global_settings").map(|a| { + let settings = a.values().into_iter().map(syn::Ident::from); + quote! { .global_settings(&[#(::clap::AppSettings::#settings),*]) } + }); + + quote! { + ::clap::App::new(#name) + #version + #author + #args + #subcommand + .about(#summary) + .after_help(#docs) + #alias + #global_settings + } +} + +pub fn expand(ast: &syn::MacroInput, attrs: &Attributes, field_attrs: &FieldAttributes) -> quote::Tokens { + let fields = match ast.body { + syn::Body::Struct(syn::VariantData::Unit) => { + Vec::new() + } + syn::Body::Struct(syn::VariantData::Struct(ref fields)) => { + fields.iter() + .map(|field| Field::from((field, field_attrs.get(field)))) + .collect() + } + syn::Body::Struct(syn::VariantData::Tuple(_)) => { + panic!("#[derive(DefineApp)] is not supported on tuple structs") + } + syn::Body::Enum(_) => { + panic!("#[derive(DefineApp)] is not supported on enums") + } + }; + + let ident = &ast.ident; + let app = expand_app(ast, attrs, &fields); + let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); + quote! { + impl #impl_generics ::clap::stomp::DefineApp for #ident #ty_generics #where_clause { + fn app() -> ::clap::App<'static, 'static> { + #app + } + } + } +} diff --git a/clap-macros/src/define_sub_commands.rs b/clap-macros/src/define_sub_commands.rs new file mode 100644 index 00000000000..878a1f438ff --- /dev/null +++ b/clap-macros/src/define_sub_commands.rs @@ -0,0 +1,46 @@ +use syn; +use quote; + +pub fn expand_subcommands(cmds: &[(&syn::Ident, &syn::Ty)]) -> quote::Tokens { + let types = cmds.iter().map(|&(_ident, ty)| ty); + quote! { vec![ #(<#types as ::clap::stomp::DefineApp>::app()),* ] } +} + +pub fn expand(ast: &syn::MacroInput) -> quote::Tokens { + let ident = &ast.ident; + + let cmds: Vec<_> = match ast.body { + syn::Body::Enum(ref variants) => { + variants.iter() + .map(|variant| match variant.data { + syn::VariantData::Tuple(ref fields) => { + if fields.len() == 1 { + (&variant.ident, &fields[0].ty) + } else { + panic!("#[derive(DefineSubCommands)] does not support enum variants with multiple fields") + } + } + syn::VariantData::Struct(_) => { + panic!("#[derive(DefineSubCommands)] does not support struct enum variants") + } + syn::VariantData::Unit => { + panic!("#[derive(DefineSubCommands)] does not support unit enum variants") + } + }) + .collect() + } + syn::Body::Struct(_) => { + panic!("#[derive(DefineSubCommands)] is not supported on structs") + } + }; + + let subcommands = expand_subcommands(&cmds); + let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); + quote! { + impl #impl_generics ::clap::stomp::DefineSubCommands for #ident #ty_generics #where_clause { + fn subcommands() -> ::std::vec::Vec<::clap::App<'static, 'static>> { + #subcommands + } + } + } +} diff --git a/clap-macros/src/field.rs b/clap-macros/src/field.rs new file mode 100644 index 00000000000..3bf7066f175 --- /dev/null +++ b/clap-macros/src/field.rs @@ -0,0 +1,156 @@ +use syn; + +use attrs::Attributes; + +pub enum Field<'a> { + Arg(Arg<'a>), + Subcommand(Subcommand<'a>), +} + +pub struct Arg<'a> { + pub ident: &'a syn::Ident, + pub name: &'a str, + pub ty: &'a syn::Ty, + pub short: Option, + pub long: Option<&'a str>, + pub value_name: Option<&'a str>, + pub index: Option, + pub summary: &'a str, + pub docs: &'a str, + pub takes_value: bool, + pub is_counter: bool, + pub multiple: bool, + pub is_optional: bool, + pub required: bool, + pub default_value: Option<&'a str>, + pub min_values: Option, + pub max_values: Option, +} + +pub struct Subcommand<'a> { + pub ident: &'a syn::Ident, + pub ty: &'a syn::Ty, + pub is_optional: bool, +} + +impl<'a> Field<'a> { + pub fn arg(&self) -> Option<&Arg> { + if let Field::Arg(ref arg) = *self { + Some(arg) + } else { + None + } + } + + pub fn subcommand(&self) -> Option<&Subcommand> { + if let Field::Subcommand(ref subcommand) = *self { + Some(subcommand) + } else { + None + } + } +} + +impl<'a> From<(&'a syn::Field, &'a Attributes)> for Field<'a> { + fn from((field, attrs): (&'a syn::Field, &'a Attributes)) -> Field<'a> { + if attrs.get_bool("subcommand") { + Field::Subcommand(Subcommand::from(field)) + } else { + Field::Arg(Arg::from((field, attrs))) + } + } +} + +impl<'a> From<(&'a syn::Field, &'a Attributes)> for Arg<'a> { + fn from((field, attrs): (&'a syn::Field, &'a Attributes)) -> Arg<'a> { + let name = attrs.get("name").map(|a| a.into()) + .unwrap_or_else(|| field.ident.as_ref().unwrap().as_ref()); + + let index = attrs.get("index").map(|a| a.into(): u64); + + // Unlike clap we default to a flag option unless there's a attribute given + // telling us to not do so + let is_flag = !index.is_some() && !attrs.get_bool("arg"); + + let long = attrs.get("long").map(|a| a.into()) + .or_else(|| if is_flag { Some(name) } else { None }); + + let short = attrs.get("short").map(|s| (s.into(): char).to_string()); + let value_name = attrs.get("value_name").map(|a| a.into()); + + let is_counter = attrs.get_bool("counted"); + + let (is_bool, is_optional, is_vec, ty); + match field.ty { + syn::Ty::Path(None, ref path) => { + is_bool = path.segments[0].ident == "bool"; + is_optional = path.segments[0].ident == "Option"; + is_vec = path.segments[0].ident == "Vec"; + if is_optional || is_vec { + if let syn::PathParameters::AngleBracketed(ref params) = path.segments[0].parameters { + ty = ¶ms.types[0]; + } else { + panic!(); + } + } else { + ty = &field.ty; + } + } + _ => panic!("unsupported field type {:?}", field.ty), + }; + + let multiple = is_counter || is_vec; + let default_value = attrs.get("default_value").map(|a| a.into()); + let min_values = attrs.get("min_values").map(|a| a.into()); + let max_values = attrs.get("max_values").map(|a| a.into()); + + let required = !is_bool && !is_optional; + + Arg { + ident: field.ident.as_ref().unwrap(), + ty: ty, + name: name, + short: short, + long: long, + index: index, + value_name: value_name, + summary: &attrs.summary, + docs: &attrs.docs, + is_counter: is_counter, + multiple: multiple, + takes_value: !is_counter && !is_bool, + is_optional: is_optional, + required: required, + default_value: default_value, + min_values: min_values, + max_values: max_values, + } + } +} + +impl<'a> From<&'a syn::Field> for Subcommand<'a> { + fn from(field: &'a syn::Field) -> Subcommand<'a> { + let (is_optional, ty); + match field.ty { + syn::Ty::Path(None, ref path) => { + is_optional = path.segments[0].ident == "Option"; + if is_optional { + if let syn::PathParameters::AngleBracketed(ref params) = path.segments[0].parameters { + ty = ¶ms.types[0]; + } else { + panic!(); + } + } else { + ty = &field.ty; + } + } + _ => panic!("unsupported field type {:?}", field.ty), + }; + + Subcommand { + ident: field.ident.as_ref().unwrap(), + ty: ty, + is_optional: is_optional, + } + } +} diff --git a/clap-macros/src/from_arg_matches.rs b/clap-macros/src/from_arg_matches.rs new file mode 100644 index 00000000000..51dd5071819 --- /dev/null +++ b/clap-macros/src/from_arg_matches.rs @@ -0,0 +1,123 @@ +use syn; +use quote; + +use attrs::FieldAttributes; +use field::{ Arg, Field, Subcommand }; + +fn expand_parse_arg(arg: &Arg, matches: &syn::Ident) -> quote::Tokens { + let ident = arg.ident; + let name = arg.name; + let value = if arg.is_counter { + quote! { #matches.occurrences_of(#name) } + } else { + if arg.takes_value { + if arg.multiple { + quote! { + #matches + .values_of(#name) + .map(|vs| vs.map(|v| v.parse().unwrap()).collect()) + .unwrap_or_else(|| Vec::new()) + } + } else { + if arg.is_optional { + quote! { + #matches + .value_of(#name) + .map(|a| a.parse().unwrap()) + } + } else { + quote! { + #matches + .value_of(#name).unwrap() + .parse().unwrap() + } + } + } + } else { + quote! { #matches.is_present(#name) } + } + }; + + quote! { + #ident: #value + } +} + +fn expand_parse_subcommand(cmd: &Subcommand, matches: &syn::Ident) -> quote::Tokens { + let ident = cmd.ident; + let ty = cmd.ty; + + let (default, wrapper); + if cmd.is_optional { + default = quote! { None }; + wrapper = Some(quote! { Some }); + } else { + default = quote! { unreachable!() }; + wrapper = None; + } + + quote! { + #ident: match #matches.subcommand() { + (name, Some(matches)) => #wrapper(<#ty as ::clap::stomp::SubCommandsFromArgMatches>::from(name, matches)), + (_, None) => #default, + } + } +} + +fn expand_parse_field(field: &Field, matches: &syn::Ident) -> quote::Tokens { + match *field { + Field::Arg(ref arg) => expand_parse_arg(arg, matches), + Field::Subcommand(ref cmd) => expand_parse_subcommand(cmd, matches), + } +} + +fn expand_parse(ast: &syn::MacroInput, fields: &[Field], matches: &syn::Ident) -> quote::Tokens { + let name = &ast.ident; + let fields = fields.iter().map(|field| expand_parse_field(field, matches)); + quote! { + #name { + #( #fields ),* + } + } +} + +pub fn expand(ast: &syn::MacroInput, field_attrs: &FieldAttributes) -> quote::Tokens { + let fields = match ast.body { + syn::Body::Struct(syn::VariantData::Unit) => { + Vec::new() + } + syn::Body::Struct(syn::VariantData::Struct(ref fields)) => { + fields.iter() + .map(|field| Field::from((field, field_attrs.get(field)))) + .collect() + } + syn::Body::Struct(syn::VariantData::Tuple(_)) => { + panic!("#[derive(FromArgMatches)] is not supported on tuple structs") + } + syn::Body::Enum(_) => { + panic!("#[derive(FromArgMatches)] is not supported on enums") + } + }; + + let ident = &ast.ident; + let matches = "matches".into(): syn::Ident; + let parse = expand_parse(ast, &fields, &matches); + let allow_unused = syn::Attribute { + style: syn::AttrStyle::Outer, + value: syn::MetaItem::List(syn::Ident::from("allow"), vec![ + syn::NestedMetaItem::MetaItem( + syn::MetaItem::Word(syn::Ident::from("unused_variables")) + ), + ]), + is_sugared_doc: false, + }; + let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); + quote! { + impl #impl_generics ::clap::stomp::FromArgMatches for #ident #ty_generics #where_clause { + #allow_unused + fn from(#matches: &::clap::ArgMatches) -> Self { + #parse + } + } + } +} diff --git a/clap-macros/src/lib.rs b/clap-macros/src/lib.rs new file mode 100644 index 00000000000..cad7eabc64f --- /dev/null +++ b/clap-macros/src/lib.rs @@ -0,0 +1,47 @@ +#![feature(type_ascription)] + +extern crate proc_macro; +extern crate syn; +#[macro_use] +extern crate quote; + +mod attr; +mod attrs; +mod field; +mod define_app; +mod from_arg_matches; +mod define_sub_commands; +mod sub_command_from_arg_matches; + +#[proc_macro_derive(DefineApp, attributes(clap))] +pub fn define_app(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let ast = syn::parse_macro_input(&input.to_string()).unwrap(); + let (attrs, field_attrs) = attrs::extract_attrs(&ast); + let expanded = define_app::expand(&ast, &attrs, &field_attrs); + attrs.check_used(ast.ident.as_ref(), None); + field_attrs.check_used(ast.ident.as_ref()); + expanded.parse().unwrap() +} + +#[proc_macro_derive(FromArgMatches, attributes(clap))] +pub fn from_arg_matches(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let ast = syn::parse_macro_input(&input.to_string()).unwrap(); + let (_, field_attrs) = attrs::extract_attrs(&ast); + let expanded = from_arg_matches::expand(&ast, &field_attrs); + field_attrs.check_used(ast.ident.as_ref()); + expanded.parse().unwrap() +} + +#[proc_macro_derive(DefineSubCommands, attributes(clap))] +pub fn define_sub_commands(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let ast = syn::parse_macro_input(&input.to_string()).unwrap(); + let expanded = define_sub_commands::expand(&ast); + expanded.parse().unwrap() +} + +#[proc_macro_derive(SubCommandFromArgMatches, attributes(clap))] +pub fn sub_command_from_arg_matches(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let ast = syn::parse_macro_input(&input.to_string()).unwrap(); + let expanded = sub_command_from_arg_matches::expand(&ast); + expanded.parse().unwrap() +} diff --git a/clap-macros/src/sub_command_from_arg_matches.rs b/clap-macros/src/sub_command_from_arg_matches.rs new file mode 100644 index 00000000000..4daf3421daf --- /dev/null +++ b/clap-macros/src/sub_command_from_arg_matches.rs @@ -0,0 +1,56 @@ +use syn; +use quote; + +fn expand_parse(me: &syn::Ident, cmds: &[(&syn::Ident, &syn::Ty)], name: &syn::Ident, matches: &syn::Ident) -> quote::Tokens { + let variants = cmds.iter().map(|&(ident, ty)| { + let name = ident.as_ref().to_lowercase(); + quote! { #name => #me::#ident(<#ty as ::clap::stomp::FromArgMatches>::from(#matches)) } + }); + quote! { + match #name { + #(#variants,)* + _ => unreachable!(), + } + } +} + +pub fn expand(ast: &syn::MacroInput) -> quote::Tokens { + let ident = &ast.ident; + let name = "name".into(): syn::Ident; + let matches = "matches".into(): syn::Ident; + + let cmds: Vec<_> = match ast.body { + syn::Body::Enum(ref variants) => { + variants.iter() + .map(|variant| match variant.data { + syn::VariantData::Tuple(ref fields) => { + if fields.len() == 1 { + (&variant.ident, &fields[0].ty) + } else { + panic!("#[derive(SubCommandFromArgMatches)] does not support enum variants with multiple fields") + } + } + syn::VariantData::Struct(_) => { + panic!("#[derive(SubCommandFromArgMatches)] does not support struct enum variants") + } + syn::VariantData::Unit => { + panic!("#[derive(SubCommandFromArgMatches)] does not support unit enum variants") + } + }) + .collect() + } + syn::Body::Struct(_) => { + panic!("#[derive(SubCommandFromArgMatches)] is not supported on structs") + } + }; + + let from = expand_parse(ident, &cmds, &name, &matches); + let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); + quote! { + impl #impl_generics ::clap::stomp::SubCommandFromArgMatches for #ident #ty_generics #where_clause { + fn from(#name: &str, #matches: &::clap::ArgMatches) -> Self { + #from + } + } + } +} diff --git a/examples/01b_quick_example_stomp.rs b/examples/01b_quick_example_stomp.rs new file mode 100644 index 00000000000..ecabcf84a32 --- /dev/null +++ b/examples/01b_quick_example_stomp.rs @@ -0,0 +1,106 @@ +#![feature(attr_literals)] +#![feature(custom_derive)] +#![feature(proc_macro)] + +extern crate clap; +#[macro_use] +extern crate clap_macros; + +use clap::stomp::*; + +// Create an application with 5 possible arguments (2 auto generated) and 2 subcommands (1 auto generated) +// - A config file +// + Uses "-c filename" or "--config filename" +// - An output file +// + A positional argument (i.e. "$ myapp output_filename") +// - A debug flag +// + Uses "-d" or "--debug" +// + Allows multiple occurrences of such as "-dd" (for vary levels of debugging, as an example) +// - A help flag (automatically generated by clap) +// + Uses "-h" or "--help" (Only autogenerated if you do NOT specify your own "-h" or "--help") +// - A version flag (automatically generated by clap) +// + Uses "-V" or "--version" (Only autogenerated if you do NOT specify your own "-V" or "--version") +// - A subcommand "test" (subcommands behave like their own apps, with their own arguments +// + Used by "$ myapp test" with the following arguments +// > A list flag +// = Uses "-l" (usage is "$ myapp test -l" +// > A help flag (automatically generated by clap +// = Uses "-h" or "--help" (full usage "$ myapp test -h" or "$ myapp test --help") +// > A version flag (automatically generated by clap +// = Uses "-V" or "--version" (full usage "$ myapp test -V" or "$ myapp test --version") +// - A subcommand "help" (automatically generated by clap because we specified a subcommand of our own) +// + Used by "$ myapp help" (same functionality as "-h" or "--help") + +/// Does awesome things. +#[derive(DefineApp, FromArgMatches)] +#[clap(name = "MyApp", version = "1.0")] +#[clap(author = "Nemo157 ")] +pub struct MyApp { + /// Sets a custom config file + #[clap(short = 'c', value_name = "FILE")] + config: Option, + + /// Sets an optional output file + #[clap(index = 1)] + output: Option, + + /// Turn debugging information on + #[clap(counted, short = 'd', long = "debug")] + debug_level: u64, + + #[clap(subcommand)] + subcommand: Option, +} + +#[derive(DefineSubCommands, SubCommandFromArgMatches)] +pub enum Commands { + Test(Test), +} + +/// does testing things +#[derive(DefineApp, FromArgMatches)] +pub struct Test { + /// lists test values + #[clap(short = 'l')] + list: bool, +} + +fn main() { + let app = MyApp::parse(); + + // You can check the value provided by positional arguments, or option arguments + if let Some(o) = app.output { + println!("Value for output: {}", o); + } + + if let Some(c) = app.config { + println!("Value for config: {}", c); + } + + // You can see how many times a particular flag or argument occurred + // Note, only flags can have multiple occurrences + match app.debug_level { + 0 => println!("Debug mode is off"), + 1 => println!("Debug mode is kind of on"), + 2 => println!("Debug mode is on"), + 3 | _ => println!("Don't be crazy"), + } + + // You can check for the existence of subcommands, and if found use their + // matches just as you would the top level app + match app.subcommand { + Some(Commands::Test(test)) => { + if test.list { + // "$ myapp test -l" was run + println!("Printing testing lists..."); + } else { + // "$ myapp test" was run + println!("Not printing testing lists..."); + } + } + None => { + } + } + + // Continued program logic goes here... +} diff --git a/src/lib.rs b/src/lib.rs index 8a1e1ffe1a8..c2119058309 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -562,6 +562,8 @@ mod errors; mod osstringext; mod strext; mod completions; +/// TODO +pub mod stomp; const INTERNAL_ERROR_MSG: &'static str = "Fatal internal error. Please consider filing a bug \ report at https://github.com/kbknapp/clap-rs/issues"; diff --git a/src/stomp.rs b/src/stomp.rs new file mode 100644 index 00000000000..bf605d16054 --- /dev/null +++ b/src/stomp.rs @@ -0,0 +1,49 @@ +use { App, ArgMatches }; + +/// TODO +pub trait DefineApp { + /// TODO + fn app() -> App<'static, 'static>; +} + +/// TODO +pub trait FromArgMatches { + /// TODO + fn from(matches: &ArgMatches) -> Self; +} + +/// TODO +pub trait DefineSubCommands { + /// TODO + fn subcommands() -> Vec>; +} + +/// TODO +pub trait SubCommandFromArgMatches { + /// TODO + fn from(name: &str, matches: &ArgMatches) -> Self; +} + +/// TODO +pub trait ParseApp { + /// TODO + fn parse() -> Self; +} + +impl ParseApp for C where C: DefineApp + FromArgMatches { + fn parse() -> Self { + C::from(&App::get_matches(C::app())) + } +} + +impl DefineSubCommands for Option where C: DefineSubCommands { + fn subcommands() -> Vec> { + C::subcommands() + } +} + +impl SubCommandFromArgMatches for Option where C: SubCommandFromArgMatches { + fn from(name: &str, matches: &ArgMatches) -> Self { + Some(C::from(name, matches)) + } +} From 33f7266eaa97ce599f119d251cb37d574cd89aae Mon Sep 17 00:00:00 2001 From: Wim Looman Date: Mon, 30 Jan 2017 19:02:54 +0100 Subject: [PATCH 2/8] Remove all feature use --- clap-macros/src/attr.rs | 11 +++++++++++ clap-macros/src/field.rs | 4 ++-- clap-macros/src/from_arg_matches.rs | 4 ++-- clap-macros/src/lib.rs | 2 -- clap-macros/src/sub_command_from_arg_matches.rs | 4 ++-- examples/01b_quick_example_stomp.rs | 12 ++++-------- 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/clap-macros/src/attr.rs b/clap-macros/src/attr.rs index 032935e37dd..d2acc524c4c 100644 --- a/clap-macros/src/attr.rs +++ b/clap-macros/src/attr.rs @@ -57,6 +57,8 @@ impl<'a> Into<&'a [u8]> for &'a Attribute { fn into(self) -> &'a [u8] { if let &syn::Lit::ByteStr(ref value, _) = self.only_value() { value + } else if let &syn::Lit::Str(ref value, _) = self.only_value() { + value.as_bytes() } else { panic!("Expected bytestring value for attribute {} but got a {:?}", self.key, self.only_value()); } @@ -67,6 +69,8 @@ impl<'a> Into for &'a Attribute { fn into(self) -> u8 { if let &syn::Lit::Byte(ref value) = self.only_value() { *value + } else if let &syn::Lit::Str(ref value, _) = self.only_value() { + value.parse().unwrap() } else { panic!("Expected byte value for attribute {} but got a {:?}", self.key, self.only_value()); } @@ -77,6 +81,9 @@ impl<'a> Into for &'a Attribute { fn into(self) -> char { if let &syn::Lit::Char(ref value) = self.only_value() { *value + } else if let &syn::Lit::Str(ref value, _) = self.only_value() { + assert!(value.len() == 1); + value.chars().next().unwrap() } else { panic!("Expected char value for attribute {} but got a {:?}", self.key, self.only_value()); } @@ -87,6 +94,8 @@ impl<'a> Into for &'a Attribute { fn into(self) -> u64 { if let &syn::Lit::Int(ref value, _) = self.only_value() { *value + } else if let &syn::Lit::Str(ref value, _) = self.only_value() { + value.parse().unwrap() } else { panic!("Expected int value for attribute {} but got a {:?}", self.key, self.only_value()); } @@ -97,6 +106,8 @@ impl<'a> Into for &'a Attribute { fn into(self) -> bool { if let &syn::Lit::Bool(ref value) = self.only_value() { *value + } else if let &syn::Lit::Str(ref value, _) = self.only_value() { + value.parse().unwrap() } else { panic!("Expected bool value for attribute {} but got a {:?}", self.key, self.only_value()); } diff --git a/clap-macros/src/field.rs b/clap-macros/src/field.rs index 3bf7066f175..9e6a680c635 100644 --- a/clap-macros/src/field.rs +++ b/clap-macros/src/field.rs @@ -66,7 +66,7 @@ impl<'a> From<(&'a syn::Field, &'a Attributes)> for Arg<'a> { let name = attrs.get("name").map(|a| a.into()) .unwrap_or_else(|| field.ident.as_ref().unwrap().as_ref()); - let index = attrs.get("index").map(|a| a.into(): u64); + let index = attrs.get("index").map(|a| a.into()); // Unlike clap we default to a flag option unless there's a attribute given // telling us to not do so @@ -75,7 +75,7 @@ impl<'a> From<(&'a syn::Field, &'a Attributes)> for Arg<'a> { let long = attrs.get("long").map(|a| a.into()) .or_else(|| if is_flag { Some(name) } else { None }); - let short = attrs.get("short").map(|s| (s.into(): char).to_string()); + let short = attrs.get("short").map(|s| Into::::into(s).to_string()); let value_name = attrs.get("value_name").map(|a| a.into()); let is_counter = attrs.get_bool("counted"); diff --git a/clap-macros/src/from_arg_matches.rs b/clap-macros/src/from_arg_matches.rs index 51dd5071819..7cc38a6deb0 100644 --- a/clap-macros/src/from_arg_matches.rs +++ b/clap-macros/src/from_arg_matches.rs @@ -58,7 +58,7 @@ fn expand_parse_subcommand(cmd: &Subcommand, matches: &syn::Ident) -> quote::Tok quote! { #ident: match #matches.subcommand() { - (name, Some(matches)) => #wrapper(<#ty as ::clap::stomp::SubCommandsFromArgMatches>::from(name, matches)), + (name, Some(matches)) => #wrapper(<#ty as ::clap::stomp::SubCommandFromArgMatches>::from(name, matches)), (_, None) => #default, } } @@ -100,7 +100,7 @@ pub fn expand(ast: &syn::MacroInput, field_attrs: &FieldAttributes) -> quote::To }; let ident = &ast.ident; - let matches = "matches".into(): syn::Ident; + let matches: syn::Ident = "matches".into(); let parse = expand_parse(ast, &fields, &matches); let allow_unused = syn::Attribute { style: syn::AttrStyle::Outer, diff --git a/clap-macros/src/lib.rs b/clap-macros/src/lib.rs index cad7eabc64f..c24c888da52 100644 --- a/clap-macros/src/lib.rs +++ b/clap-macros/src/lib.rs @@ -1,5 +1,3 @@ -#![feature(type_ascription)] - extern crate proc_macro; extern crate syn; #[macro_use] diff --git a/clap-macros/src/sub_command_from_arg_matches.rs b/clap-macros/src/sub_command_from_arg_matches.rs index 4daf3421daf..4c3f5065794 100644 --- a/clap-macros/src/sub_command_from_arg_matches.rs +++ b/clap-macros/src/sub_command_from_arg_matches.rs @@ -16,8 +16,8 @@ fn expand_parse(me: &syn::Ident, cmds: &[(&syn::Ident, &syn::Ty)], name: &syn::I pub fn expand(ast: &syn::MacroInput) -> quote::Tokens { let ident = &ast.ident; - let name = "name".into(): syn::Ident; - let matches = "matches".into(): syn::Ident; + let name: syn::Ident = "name".into(); + let matches: syn::Ident = "matches".into(); let cmds: Vec<_> = match ast.body { syn::Body::Enum(ref variants) => { diff --git a/examples/01b_quick_example_stomp.rs b/examples/01b_quick_example_stomp.rs index ecabcf84a32..b8477c25592 100644 --- a/examples/01b_quick_example_stomp.rs +++ b/examples/01b_quick_example_stomp.rs @@ -1,7 +1,3 @@ -#![feature(attr_literals)] -#![feature(custom_derive)] -#![feature(proc_macro)] - extern crate clap; #[macro_use] extern crate clap_macros; @@ -37,15 +33,15 @@ use clap::stomp::*; #[clap(author = "Nemo157 ")] pub struct MyApp { /// Sets a custom config file - #[clap(short = 'c', value_name = "FILE")] + #[clap(short = "c", value_name = "FILE")] config: Option, /// Sets an optional output file - #[clap(index = 1)] + #[clap(index = "1")] output: Option, /// Turn debugging information on - #[clap(counted, short = 'd', long = "debug")] + #[clap(counted, short = "d", long = "debug")] debug_level: u64, #[clap(subcommand)] @@ -61,7 +57,7 @@ pub enum Commands { #[derive(DefineApp, FromArgMatches)] pub struct Test { /// lists test values - #[clap(short = 'l')] + #[clap(short = "l")] list: bool, } From bb0d7bd67b20fa8210078a51399ce92f7a577d36 Mon Sep 17 00:00:00 2001 From: Wim Looman Date: Mon, 30 Jan 2017 22:55:21 +0100 Subject: [PATCH 3/8] Cleanup * stomp -> code_gen * DefineX -> X * Single derive for SubCommands and SubCommandsFromArgMatches * Use From<&ArgMatches> instead of special FromArgMatches --- clap-macros/src/define_app.rs | 4 +- clap-macros/src/define_sub_commands.rs | 4 +- clap-macros/src/from_arg_matches.rs | 32 ++++++------ clap-macros/src/lib.rs | 20 +++----- .../src/sub_command_from_arg_matches.rs | 6 +-- examples/01b_quick_example_stomp.rs | 10 ++-- src/code_gen.rs | 31 ++++++++++++ src/lib.rs | 2 +- src/stomp.rs | 49 ------------------- 9 files changed, 68 insertions(+), 90 deletions(-) create mode 100644 src/code_gen.rs delete mode 100644 src/stomp.rs diff --git a/clap-macros/src/define_app.rs b/clap-macros/src/define_app.rs index 23168841edb..89d64ad092a 100644 --- a/clap-macros/src/define_app.rs +++ b/clap-macros/src/define_app.rs @@ -61,7 +61,7 @@ fn expand_subcommand(subcommand: &Subcommand) -> quote::Tokens { }; quote! { - .subcommands(<#ty as ::clap::stomp::DefineSubCommands>::subcommands()) + .subcommands(<#ty as ::clap::code_gen::SubCommands>::subcommands()) #required } } @@ -131,7 +131,7 @@ pub fn expand(ast: &syn::MacroInput, attrs: &Attributes, field_attrs: &FieldAttr let app = expand_app(ast, attrs, &fields); let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); quote! { - impl #impl_generics ::clap::stomp::DefineApp for #ident #ty_generics #where_clause { + impl #impl_generics ::clap::code_gen::App for #ident #ty_generics #where_clause { fn app() -> ::clap::App<'static, 'static> { #app } diff --git a/clap-macros/src/define_sub_commands.rs b/clap-macros/src/define_sub_commands.rs index 878a1f438ff..e188bd97788 100644 --- a/clap-macros/src/define_sub_commands.rs +++ b/clap-macros/src/define_sub_commands.rs @@ -3,7 +3,7 @@ use quote; pub fn expand_subcommands(cmds: &[(&syn::Ident, &syn::Ty)]) -> quote::Tokens { let types = cmds.iter().map(|&(_ident, ty)| ty); - quote! { vec![ #(<#types as ::clap::stomp::DefineApp>::app()),* ] } + quote! { vec![ #(<#types as ::clap::code_gen::App>::app()),* ] } } pub fn expand(ast: &syn::MacroInput) -> quote::Tokens { @@ -37,7 +37,7 @@ pub fn expand(ast: &syn::MacroInput) -> quote::Tokens { let subcommands = expand_subcommands(&cmds); let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); quote! { - impl #impl_generics ::clap::stomp::DefineSubCommands for #ident #ty_generics #where_clause { + impl #impl_generics ::clap::code_gen::SubCommands for #ident #ty_generics #where_clause { fn subcommands() -> ::std::vec::Vec<::clap::App<'static, 'static>> { #subcommands } diff --git a/clap-macros/src/from_arg_matches.rs b/clap-macros/src/from_arg_matches.rs index 7cc38a6deb0..3226e083b84 100644 --- a/clap-macros/src/from_arg_matches.rs +++ b/clap-macros/src/from_arg_matches.rs @@ -58,7 +58,7 @@ fn expand_parse_subcommand(cmd: &Subcommand, matches: &syn::Ident) -> quote::Tok quote! { #ident: match #matches.subcommand() { - (name, Some(matches)) => #wrapper(<#ty as ::clap::stomp::SubCommandFromArgMatches>::from(name, matches)), + (name, Some(matches)) => #wrapper(<#ty as ::clap::code_gen::SubCommandFromArgMatches>::from_matches(name, matches)), (_, None) => #default, } } @@ -100,22 +100,24 @@ pub fn expand(ast: &syn::MacroInput, field_attrs: &FieldAttributes) -> quote::To }; let ident = &ast.ident; - let matches: syn::Ident = "matches".into(); + let matches = syn::Ident::new("matches"); let parse = expand_parse(ast, &fields, &matches); - let allow_unused = syn::Attribute { - style: syn::AttrStyle::Outer, - value: syn::MetaItem::List(syn::Ident::from("allow"), vec![ - syn::NestedMetaItem::MetaItem( - syn::MetaItem::Word(syn::Ident::from("unused_variables")) - ), - ]), - is_sugared_doc: false, - }; - let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); + // Is this really the best way to add extra lifetimes D: + // Also, hygiene is important... + let a = syn::LifetimeDef::new("'clap_macros_from_arg_matches_a"); + let mut b = syn::LifetimeDef::new("'clap_macros_from_arg_matches_b"); + b.bounds.push(a.lifetime.clone()); + let mut generics = ast.generics.clone(); + generics.lifetimes.push(a.clone()); + generics.lifetimes.push(b.clone()); + let (impl_generics, _, _) = generics.split_for_impl(); + let (_, ty_generics, where_clause) = ast.generics.split_for_impl(); + let a = a.lifetime; + let b = b.lifetime; quote! { - impl #impl_generics ::clap::stomp::FromArgMatches for #ident #ty_generics #where_clause { - #allow_unused - fn from(#matches: &::clap::ArgMatches) -> Self { + impl #impl_generics ::std::convert::From<&#a ::clap::ArgMatches<#b>> for #ident #ty_generics #where_clause { + #[allow(unused)] + fn from(#matches: &#a ::clap::ArgMatches<#b>) -> Self { #parse } } diff --git a/clap-macros/src/lib.rs b/clap-macros/src/lib.rs index c24c888da52..55b9d2222d9 100644 --- a/clap-macros/src/lib.rs +++ b/clap-macros/src/lib.rs @@ -11,8 +11,8 @@ mod from_arg_matches; mod define_sub_commands; mod sub_command_from_arg_matches; -#[proc_macro_derive(DefineApp, attributes(clap))] -pub fn define_app(input: proc_macro::TokenStream) -> proc_macro::TokenStream { +#[proc_macro_derive(App, attributes(clap))] +pub fn app(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse_macro_input(&input.to_string()).unwrap(); let (attrs, field_attrs) = attrs::extract_attrs(&ast); let expanded = define_app::expand(&ast, &attrs, &field_attrs); @@ -30,16 +30,10 @@ pub fn from_arg_matches(input: proc_macro::TokenStream) -> proc_macro::TokenStre expanded.parse().unwrap() } -#[proc_macro_derive(DefineSubCommands, attributes(clap))] -pub fn define_sub_commands(input: proc_macro::TokenStream) -> proc_macro::TokenStream { +#[proc_macro_derive(SubCommands, attributes(clap))] +pub fn subcommands(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse_macro_input(&input.to_string()).unwrap(); - let expanded = define_sub_commands::expand(&ast); - expanded.parse().unwrap() -} - -#[proc_macro_derive(SubCommandFromArgMatches, attributes(clap))] -pub fn sub_command_from_arg_matches(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let ast = syn::parse_macro_input(&input.to_string()).unwrap(); - let expanded = sub_command_from_arg_matches::expand(&ast); - expanded.parse().unwrap() + let subcommands = define_sub_commands::expand(&ast); + let from_arg_matches = sub_command_from_arg_matches::expand(&ast); + quote!(#subcommands #from_arg_matches).to_string().parse().unwrap() } diff --git a/clap-macros/src/sub_command_from_arg_matches.rs b/clap-macros/src/sub_command_from_arg_matches.rs index 4c3f5065794..78ef9ec23c3 100644 --- a/clap-macros/src/sub_command_from_arg_matches.rs +++ b/clap-macros/src/sub_command_from_arg_matches.rs @@ -4,7 +4,7 @@ use quote; fn expand_parse(me: &syn::Ident, cmds: &[(&syn::Ident, &syn::Ty)], name: &syn::Ident, matches: &syn::Ident) -> quote::Tokens { let variants = cmds.iter().map(|&(ident, ty)| { let name = ident.as_ref().to_lowercase(); - quote! { #name => #me::#ident(<#ty as ::clap::stomp::FromArgMatches>::from(#matches)) } + quote! { #name => #me::#ident(<#ty as ::std::convert::From<&::clap::ArgMatches>>::from(#matches)) } }); quote! { match #name { @@ -47,8 +47,8 @@ pub fn expand(ast: &syn::MacroInput) -> quote::Tokens { let from = expand_parse(ident, &cmds, &name, &matches); let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); quote! { - impl #impl_generics ::clap::stomp::SubCommandFromArgMatches for #ident #ty_generics #where_clause { - fn from(#name: &str, #matches: &::clap::ArgMatches) -> Self { + impl #impl_generics ::clap::code_gen::SubCommandFromArgMatches for #ident #ty_generics #where_clause { + fn from_matches(#name: &str, #matches: &::clap::ArgMatches) -> Self { #from } } diff --git a/examples/01b_quick_example_stomp.rs b/examples/01b_quick_example_stomp.rs index b8477c25592..a42f3844117 100644 --- a/examples/01b_quick_example_stomp.rs +++ b/examples/01b_quick_example_stomp.rs @@ -2,7 +2,7 @@ extern crate clap; #[macro_use] extern crate clap_macros; -use clap::stomp::*; +use clap::code_gen::*; // Create an application with 5 possible arguments (2 auto generated) and 2 subcommands (1 auto generated) // - A config file @@ -28,7 +28,7 @@ use clap::stomp::*; // + Used by "$ myapp help" (same functionality as "-h" or "--help") /// Does awesome things. -#[derive(DefineApp, FromArgMatches)] +#[derive(App, FromArgMatches)] #[clap(name = "MyApp", version = "1.0")] #[clap(author = "Nemo157 ")] pub struct MyApp { @@ -48,13 +48,13 @@ pub struct MyApp { subcommand: Option, } -#[derive(DefineSubCommands, SubCommandFromArgMatches)] +#[derive(SubCommands)] pub enum Commands { Test(Test), } /// does testing things -#[derive(DefineApp, FromArgMatches)] +#[derive(App, FromArgMatches)] pub struct Test { /// lists test values #[clap(short = "l")] @@ -62,7 +62,7 @@ pub struct Test { } fn main() { - let app = MyApp::parse(); + let app = MyApp::from(&MyApp::app().get_matches()); // You can check the value provided by positional arguments, or option arguments if let Some(o) = app.output { diff --git a/src/code_gen.rs b/src/code_gen.rs new file mode 100644 index 00000000000..54893883886 --- /dev/null +++ b/src/code_gen.rs @@ -0,0 +1,31 @@ +use ArgMatches; + +/// TODO +pub trait App { + /// TODO + fn app() -> ::App<'static, 'static>; +} + +/// TODO +pub trait SubCommands { + /// TODO + fn subcommands() -> Vec<::App<'static, 'static>>; +} + +/// TODO +pub trait SubCommandFromArgMatches { + /// TODO + fn from_matches(name: &str, matches: &ArgMatches) -> Self; +} + +impl SubCommands for Option where C: SubCommands { + fn subcommands() -> Vec<::App<'static, 'static>> { + C::subcommands() + } +} + +impl SubCommandFromArgMatches for Option where C: SubCommandFromArgMatches { + fn from_matches(name: &str, matches: &ArgMatches) -> Self { + Some(C::from_matches(name, matches)) + } +} diff --git a/src/lib.rs b/src/lib.rs index c2119058309..31b9cec28db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -563,7 +563,7 @@ mod osstringext; mod strext; mod completions; /// TODO -pub mod stomp; +pub mod code_gen; const INTERNAL_ERROR_MSG: &'static str = "Fatal internal error. Please consider filing a bug \ report at https://github.com/kbknapp/clap-rs/issues"; diff --git a/src/stomp.rs b/src/stomp.rs deleted file mode 100644 index bf605d16054..00000000000 --- a/src/stomp.rs +++ /dev/null @@ -1,49 +0,0 @@ -use { App, ArgMatches }; - -/// TODO -pub trait DefineApp { - /// TODO - fn app() -> App<'static, 'static>; -} - -/// TODO -pub trait FromArgMatches { - /// TODO - fn from(matches: &ArgMatches) -> Self; -} - -/// TODO -pub trait DefineSubCommands { - /// TODO - fn subcommands() -> Vec>; -} - -/// TODO -pub trait SubCommandFromArgMatches { - /// TODO - fn from(name: &str, matches: &ArgMatches) -> Self; -} - -/// TODO -pub trait ParseApp { - /// TODO - fn parse() -> Self; -} - -impl ParseApp for C where C: DefineApp + FromArgMatches { - fn parse() -> Self { - C::from(&App::get_matches(C::app())) - } -} - -impl DefineSubCommands for Option where C: DefineSubCommands { - fn subcommands() -> Vec> { - C::subcommands() - } -} - -impl SubCommandFromArgMatches for Option where C: SubCommandFromArgMatches { - fn from(name: &str, matches: &ArgMatches) -> Self { - Some(C::from(name, matches)) - } -} From dd68d609076bac957973591f2e606565c8920fd3 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Mon, 30 Jan 2017 19:18:13 -0500 Subject: [PATCH 4/8] style(clap_macros): impls Default for Attributes --- clap-macros/src/attrs.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/clap-macros/src/attrs.rs b/clap-macros/src/attrs.rs index cd125f00a5c..049eb210a74 100644 --- a/clap-macros/src/attrs.rs +++ b/clap-macros/src/attrs.rs @@ -16,7 +16,17 @@ pub struct FieldAttributes { map: HashMap, Attributes)>, } +impl Default for Attributes { + fn default() -> Self { + Attributes { summary: "".into(), docs: "".into(), map: BTreeMap::new() } + } +} + impl Attributes { + pub fn new() -> Self { + Default::default() + } + pub fn check_used(&self, name: &str, field: Option<&str>) { for (ref attr, &(ref counter, _)) in &self.map { if *counter.borrow() == 0 { @@ -133,7 +143,7 @@ fn extract_attrs_inner(attrs: &Vec) -> Attributes { /// Extracts all clap attributes of the form #[clap(i = V)] pub fn extract_attrs(ast: &syn::MacroInput) -> (Attributes, FieldAttributes) { - let empty = Attributes { summary: "".into(), docs: "".into(), map: BTreeMap::new() }; + let empty = Attributes::new(); let root_attrs = extract_attrs_inner(&ast.attrs); let field_attrs = match ast.body { syn::Body::Struct(syn::VariantData::Struct(ref fields)) => { From 544993c6412b19b2628e3e183b6c22c3a4a9d027 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Mon, 30 Jan 2017 19:21:37 -0500 Subject: [PATCH 5/8] style(clap_macros): moves extract_attrs to Attributes::from_attrs --- clap-macros/src/attrs.rs | 136 +++++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/clap-macros/src/attrs.rs b/clap-macros/src/attrs.rs index 049eb210a74..cb555787d68 100644 --- a/clap-macros/src/attrs.rs +++ b/clap-macros/src/attrs.rs @@ -27,6 +27,73 @@ impl Attributes { Default::default() } + pub fn from_attrs(attrs: &[syn::Attribute]) -> Self { + let mut claps = BTreeMap::new(); + for attr in attrs { + if let syn::MetaItem::List(ref ident, ref values) = attr.value { + if ident == "clap" { + for value in values { + match *value { + syn::NestedMetaItem::MetaItem(ref item) => match *item { + syn::MetaItem::NameValue(ref name, ref value) => { + let &mut (_, ref mut attr) = claps.entry(name.to_string()).or_insert((RefCell::new(0), Attribute::new(name.to_string()))); + attr.push(value.clone()); + } + syn::MetaItem::Word(ref name) => { + let &mut (_, ref mut attr) = claps.entry(name.to_string()).or_insert((RefCell::new(0), Attribute::new(name.to_string()))); + attr.push(syn::Lit::Bool(true)); + } + syn::MetaItem::List(ref ident, ref values) => { + let &mut (_, ref mut attr) = claps.entry(ident.as_ref().to_string()).or_insert((RefCell::new(0), Attribute::new(ident.as_ref().to_string()))); + for value in values { + match *value { + syn::NestedMetaItem::MetaItem(ref item) => match *item { + syn::MetaItem::Word(ref name) => { + attr.push(name.as_ref().into()); + } + syn::MetaItem::NameValue(..) => { + panic!("Invalid clap attribute {} named value in sublist not supported", quote!(#attr).to_string().replace(" ", "")); + } + syn::MetaItem::List(..) => { + panic!("Invalid clap attribute {} sublist in sublist not supported", quote!(#attr).to_string().replace(" ", "")); + } + }, + syn::NestedMetaItem::Literal(_) => { + panic!("Invalid clap attribute {} literal value not supported", quote!(#attr).to_string().replace(" ", "")); + }, + } + } + } + }, + syn::NestedMetaItem::Literal(_) => { + panic!("Invalid clap attribute {} literal value not supported", quote!(#attr).to_string().replace(" ", "")); + }, + } + } + } + } + } + + let docs = attrs.iter() + .filter(|a| a.is_sugared_doc) + .map(|a| match a.value { + syn::MetaItem::NameValue(_, syn::Lit::Str(ref doc, _)) => doc, + _ => unreachable!(), + }) + .fold(String::new(), |docs, line| docs + line.trim_left_matches('/').trim() + "\n"); + + let index = docs.find("\n\n"); + let (summary, docs) = if let Some(index) = index { + let (summary, docs) = docs.split_at(index); + let (_, docs) = docs.split_at(2); + (summary.into(), docs.into()) + } else { + (docs, "".into()) + }; + + Attributes { summary: summary, docs: docs, map: claps } + } + pub fn check_used(&self, name: &str, field: Option<&str>) { for (ref attr, &(ref counter, _)) in &self.map { if *counter.borrow() == 0 { @@ -74,77 +141,10 @@ impl FieldAttributes { } } -fn extract_attrs_inner(attrs: &Vec) -> Attributes { - let mut claps = BTreeMap::new(); - for attr in attrs { - if let syn::MetaItem::List(ref ident, ref values) = attr.value { - if ident == "clap" { - for value in values { - match *value { - syn::NestedMetaItem::MetaItem(ref item) => match *item { - syn::MetaItem::NameValue(ref name, ref value) => { - let &mut (_, ref mut attr) = claps.entry(name.to_string()).or_insert((RefCell::new(0), Attribute::new(name.to_string()))); - attr.push(value.clone()); - } - syn::MetaItem::Word(ref name) => { - let &mut (_, ref mut attr) = claps.entry(name.to_string()).or_insert((RefCell::new(0), Attribute::new(name.to_string()))); - attr.push(syn::Lit::Bool(true)); - } - syn::MetaItem::List(ref ident, ref values) => { - let &mut (_, ref mut attr) = claps.entry(ident.as_ref().to_string()).or_insert((RefCell::new(0), Attribute::new(ident.as_ref().to_string()))); - for value in values { - match *value { - syn::NestedMetaItem::MetaItem(ref item) => match *item { - syn::MetaItem::Word(ref name) => { - attr.push(name.as_ref().into()); - } - syn::MetaItem::NameValue(..) => { - panic!("Invalid clap attribute {} named value in sublist not supported", quote!(#attr).to_string().replace(" ", "")); - } - syn::MetaItem::List(..) => { - panic!("Invalid clap attribute {} sublist in sublist not supported", quote!(#attr).to_string().replace(" ", "")); - } - }, - syn::NestedMetaItem::Literal(_) => { - panic!("Invalid clap attribute {} literal value not supported", quote!(#attr).to_string().replace(" ", "")); - }, - } - } - } - }, - syn::NestedMetaItem::Literal(_) => { - panic!("Invalid clap attribute {} literal value not supported", quote!(#attr).to_string().replace(" ", "")); - }, - } - } - } - } - } - - let docs = attrs.iter() - .filter(|a| a.is_sugared_doc) - .map(|a| match a.value { - syn::MetaItem::NameValue(_, syn::Lit::Str(ref doc, _)) => doc, - _ => unreachable!(), - }) - .fold(String::new(), |docs, line| docs + line.trim_left_matches('/').trim() + "\n"); - - let index = docs.find("\n\n"); - let (summary, docs) = if let Some(index) = index { - let (summary, docs) = docs.split_at(index); - let (_, docs) = docs.split_at(2); - (summary.into(), docs.into()) - } else { - (docs, "".into()) - }; - - Attributes { summary: summary, docs: docs, map: claps } -} - /// Extracts all clap attributes of the form #[clap(i = V)] pub fn extract_attrs(ast: &syn::MacroInput) -> (Attributes, FieldAttributes) { let empty = Attributes::new(); - let root_attrs = extract_attrs_inner(&ast.attrs); + let root_attrs = Attributes::from_attrs(&ast.attrs); let field_attrs = match ast.body { syn::Body::Struct(syn::VariantData::Struct(ref fields)) => { fields From a6587a9a632e659c81ee524eb498e9c362da7ee4 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Mon, 30 Jan 2017 19:56:58 -0500 Subject: [PATCH 6/8] style(clap_macros): simplifies and removes some rightward drift --- clap-macros/.gitignore | 26 ++++ clap-macros/src/attrs.rs | 140 ++++++++++-------- clap-macros/target/debug/.cargo-lock | 0 .../dep-lib-clap_macros-0156bd104f722f8d | Bin 0 -> 742 bytes .../lib-clap_macros-0156bd104f722f8d | 1 + .../lib-clap_macros-0156bd104f722f8d.json | 1 + .../dep-lib-quote-b64bd59975f476de | Bin 0 -> 1264 bytes .../lib-quote-b64bd59975f476de | 1 + .../lib-quote-b64bd59975f476de.json | 1 + .../quote-c33138b2a30d7c05/dep-lib-quote | Bin 0 -> 1264 bytes .../quote-c33138b2a30d7c05/lib-quote | 1 + .../quote-c33138b2a30d7c05/lib-quote.json | 1 + .../syn-2cef086c5d2264c4/dep-lib-syn | Bin 0 -> 3989 bytes .../.fingerprint/syn-2cef086c5d2264c4/lib-syn | 1 + .../syn-2cef086c5d2264c4/lib-syn.json | 1 + .../dep-lib-syn-9d6699ed04adcefd | Bin 0 -> 3989 bytes .../lib-syn-9d6699ed04adcefd | 1 + .../lib-syn-9d6699ed04adcefd.json | 1 + .../dep-lib-unicode_xid-0718538d6479f922 | Bin 0 -> 776 bytes .../lib-unicode_xid-0718538d6479f922 | 1 + .../lib-unicode_xid-0718538d6479f922.json | 1 + .../dep-lib-unicode-xid | Bin 0 -> 776 bytes .../lib-unicode-xid | 1 + .../lib-unicode-xid.json | 1 + 24 files changed, 119 insertions(+), 61 deletions(-) create mode 100644 clap-macros/.gitignore create mode 100644 clap-macros/target/debug/.cargo-lock create mode 100644 clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/dep-lib-clap_macros-0156bd104f722f8d create mode 100644 clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/lib-clap_macros-0156bd104f722f8d create mode 100644 clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/lib-clap_macros-0156bd104f722f8d.json create mode 100644 clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/dep-lib-quote-b64bd59975f476de create mode 100644 clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/lib-quote-b64bd59975f476de create mode 100644 clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/lib-quote-b64bd59975f476de.json create mode 100644 clap-macros/target/debug/.fingerprint/quote-c33138b2a30d7c05/dep-lib-quote create mode 100644 clap-macros/target/debug/.fingerprint/quote-c33138b2a30d7c05/lib-quote create mode 100644 clap-macros/target/debug/.fingerprint/quote-c33138b2a30d7c05/lib-quote.json create mode 100644 clap-macros/target/debug/.fingerprint/syn-2cef086c5d2264c4/dep-lib-syn create mode 100644 clap-macros/target/debug/.fingerprint/syn-2cef086c5d2264c4/lib-syn create mode 100644 clap-macros/target/debug/.fingerprint/syn-2cef086c5d2264c4/lib-syn.json create mode 100644 clap-macros/target/debug/.fingerprint/syn-9d6699ed04adcefd/dep-lib-syn-9d6699ed04adcefd create mode 100644 clap-macros/target/debug/.fingerprint/syn-9d6699ed04adcefd/lib-syn-9d6699ed04adcefd create mode 100644 clap-macros/target/debug/.fingerprint/syn-9d6699ed04adcefd/lib-syn-9d6699ed04adcefd.json create mode 100644 clap-macros/target/debug/.fingerprint/unicode-xid-0718538d6479f922/dep-lib-unicode_xid-0718538d6479f922 create mode 100644 clap-macros/target/debug/.fingerprint/unicode-xid-0718538d6479f922/lib-unicode_xid-0718538d6479f922 create mode 100644 clap-macros/target/debug/.fingerprint/unicode-xid-0718538d6479f922/lib-unicode_xid-0718538d6479f922.json create mode 100644 clap-macros/target/debug/.fingerprint/unicode-xid-13fc4e6b803f75ce/dep-lib-unicode-xid create mode 100644 clap-macros/target/debug/.fingerprint/unicode-xid-13fc4e6b803f75ce/lib-unicode-xid create mode 100644 clap-macros/target/debug/.fingerprint/unicode-xid-13fc4e6b803f75ce/lib-unicode-xid.json diff --git a/clap-macros/.gitignore b/clap-macros/.gitignore new file mode 100644 index 00000000000..11101efcfb3 --- /dev/null +++ b/clap-macros/.gitignore @@ -0,0 +1,26 @@ +# Compiled files +*.o +*.so +*.rlib +*.dll + +# Executables +*.exe + +# Generated by Cargo +/target/ +/clap-test/target/ + +# Cargo files +Cargo.lock + +# Temp files +.*~ + +# Backup files +*.bak +*.bk +*.orig + +# Project files +.vscode/* diff --git a/clap-macros/src/attrs.rs b/clap-macros/src/attrs.rs index cb555787d68..55e4e6c39be 100644 --- a/clap-macros/src/attrs.rs +++ b/clap-macros/src/attrs.rs @@ -1,5 +1,5 @@ use std::cell::RefCell; -use std::collections::{ BTreeMap, HashMap }; +use std::collections::{BTreeMap, HashMap}; use syn; @@ -18,56 +18,58 @@ pub struct FieldAttributes { impl Default for Attributes { fn default() -> Self { - Attributes { summary: "".into(), docs: "".into(), map: BTreeMap::new() } + Attributes { + summary: "".into(), + docs: "".into(), + map: BTreeMap::new(), + } } } impl Attributes { - pub fn new() -> Self { - Default::default() - } + pub fn new() -> Self { Default::default() } pub fn from_attrs(attrs: &[syn::Attribute]) -> Self { + use syn::NestedMetaItem::*; + use syn::MetaItem::*; let mut claps = BTreeMap::new(); for attr in attrs { if let syn::MetaItem::List(ref ident, ref values) = attr.value { - if ident == "clap" { - for value in values { - match *value { - syn::NestedMetaItem::MetaItem(ref item) => match *item { - syn::MetaItem::NameValue(ref name, ref value) => { - let &mut (_, ref mut attr) = claps.entry(name.to_string()).or_insert((RefCell::new(0), Attribute::new(name.to_string()))); - attr.push(value.clone()); - } - syn::MetaItem::Word(ref name) => { - let &mut (_, ref mut attr) = claps.entry(name.to_string()).or_insert((RefCell::new(0), Attribute::new(name.to_string()))); - attr.push(syn::Lit::Bool(true)); - } - syn::MetaItem::List(ref ident, ref values) => { - let &mut (_, ref mut attr) = claps.entry(ident.as_ref().to_string()).or_insert((RefCell::new(0), Attribute::new(ident.as_ref().to_string()))); - for value in values { - match *value { - syn::NestedMetaItem::MetaItem(ref item) => match *item { - syn::MetaItem::Word(ref name) => { - attr.push(name.as_ref().into()); - } - syn::MetaItem::NameValue(..) => { - panic!("Invalid clap attribute {} named value in sublist not supported", quote!(#attr).to_string().replace(" ", "")); - } - syn::MetaItem::List(..) => { - panic!("Invalid clap attribute {} sublist in sublist not supported", quote!(#attr).to_string().replace(" ", "")); - } - }, - syn::NestedMetaItem::Literal(_) => { - panic!("Invalid clap attribute {} literal value not supported", quote!(#attr).to_string().replace(" ", "")); - }, - } + if ident != "clap" { + panic!("Attribute other than #[clap(..)] found"); + } + for value in values { + match *value { + // #[foo = "bar"] + MetaItem(NameValue(ref name, ref value)) => { + let &mut (_, ref mut attr) = claps.entry(name.to_string()) + .or_insert((RefCell::new(0), Attribute::new(name.to_string()))); + attr.push(value.clone()); + } + // #[foo] + MetaItem(Word(ref name)) => { + let &mut (_, ref mut attr) = claps.entry(name.to_string()) + .or_insert((RefCell::new(0), Attribute::new(name.to_string()))); + attr.push(syn::Lit::Bool(true)); + } + // #[derive(..)] + MetaItem(List(ref ident, ref values)) => { + let &mut (_, ref mut attr) = claps.entry(ident.as_ref().to_string()) + .or_insert((RefCell::new(0), + Attribute::new(ident.as_ref().to_string()))); + for value in values { + match *value { + MetaItem(Word(ref name)) => attr.push(name.as_ref().into()), + _ => { + panic!("Invalid clap attribute {} literal value not supported", + quote!(#attr).to_string().replace(" ", "")) } } - }, - syn::NestedMetaItem::Literal(_) => { - panic!("Invalid clap attribute {} literal value not supported", quote!(#attr).to_string().replace(" ", "")); - }, + } + } + _ => { + panic!("Invalid clap attribute {} literal value not supported", + quote!(#attr).to_string().replace(" ", "")) } } } @@ -80,7 +82,8 @@ impl Attributes { syn::MetaItem::NameValue(_, syn::Lit::Str(ref doc, _)) => doc, _ => unreachable!(), }) - .fold(String::new(), |docs, line| docs + line.trim_left_matches('/').trim() + "\n"); + .fold(String::new(), + |docs, line| docs + line.trim_left_matches('/').trim() + "\n"); let index = docs.find("\n\n"); let (summary, docs) = if let Some(index) = index { @@ -91,17 +94,28 @@ impl Attributes { (docs, "".into()) }; - Attributes { summary: summary, docs: docs, map: claps } + Attributes { + summary: summary, + docs: docs, + map: claps, + } } pub fn check_used(&self, name: &str, field: Option<&str>) { for (ref attr, &(ref counter, _)) in &self.map { if *counter.borrow() == 0 { match field { - Some(field) => - println!("clap-macros: unexpected attribute '{}' on field '{}' of struct '{}'", attr, field, name), - None => - println!("clap-macros: unexpected attribute '{}' on struct '{}'", attr, name), + Some(field) => { + println!("clap-macros: unexpected attribute '{}' on field '{}' of struct '{}'", + attr, + field, + name) + } + None => { + println!("clap-macros: unexpected attribute '{}' on struct '{}'", + attr, + name) + } } } } @@ -116,16 +130,16 @@ impl Attributes { } } - pub fn get_bool(&self, key: &str) -> bool { - self.get(key).map(|a| a.into()).unwrap_or(false) - } + pub fn get_bool(&self, key: &str) -> bool { self.get(key).map(|a| a.into()).unwrap_or(false) } } impl FieldAttributes { pub fn check_used(&self, name: &str) { for (ref field, &(ref counter, ref attrs)) in &self.map { if *counter.borrow() == 0 { - panic!("clap-macros: didn't access attributes for field '{}' on struct '{}' for some reason", field, name); + panic!("clap-macros: didn't access attributes for field '{}' on struct '{}' for some reason", + field, + name); } attrs.check_used(name, Some(field.as_ref())); } @@ -143,21 +157,25 @@ impl FieldAttributes { /// Extracts all clap attributes of the form #[clap(i = V)] pub fn extract_attrs(ast: &syn::MacroInput) -> (Attributes, FieldAttributes) { + use syn::Body::*; + use syn::VariantData::*; let empty = Attributes::new(); let root_attrs = Attributes::from_attrs(&ast.attrs); let field_attrs = match ast.body { - syn::Body::Struct(syn::VariantData::Struct(ref fields)) => { - fields - .iter() - .map(|field| (field.ident.clone().unwrap(), (RefCell::new(0), extract_attrs_inner(&field.attrs)))) + Struct(Struct(ref fields)) => { + fields.iter() + .map(|field| { + (field.ident.clone().unwrap(), + (RefCell::new(0), Attributes::from_attrs(&field.attrs))) + }) .collect() } - syn::Body::Struct(syn::VariantData::Tuple(_)) => { - panic!("TODO: tuple struct unsupported msg") - } - syn::Body::Struct(syn::VariantData::Unit) | syn::Body::Enum(_) => { - HashMap::new() - } + Struct(Tuple(_)) => panic!("TODO: tuple struct unsupported msg"), + Struct(Unit) | Enum(_) => HashMap::new(), }; - (root_attrs, FieldAttributes { empty: empty, map: field_attrs }) + (root_attrs, + FieldAttributes { + empty: empty, + map: field_attrs, + }) } diff --git a/clap-macros/target/debug/.cargo-lock b/clap-macros/target/debug/.cargo-lock new file mode 100644 index 00000000000..e69de29bb2d diff --git a/clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/dep-lib-clap_macros-0156bd104f722f8d b/clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/dep-lib-clap_macros-0156bd104f722f8d new file mode 100644 index 0000000000000000000000000000000000000000..106e884850f0084f248c6a4d3f5898880d884908 GIT binary patch literal 742 zcmdUsO%8(~5QV$;6up3=rhoAiy#YZO)K-K5_Wac%im7Rubkz;a8y*9BAF8DQbcaXN zp{tfRpw=MT5*6!d7ldkQcIZnZvP4&45reX?hE*BVHsv@GyGe1H9Zx08(gQ!|Ilr*P z$YO6a9es&5uhLn)BG&4#HeT=sTIK}<-gJP8Qoh5r6nOZ~@Uo>f%-1HT=}Szd5Ts-C zQP9I1*Q4l%96pA?{wRY5RuPT$Ov}j$*CNJro$|n(S`nKJ@5ol(fX^~DXl_9(q9-v4 B58MC% literal 0 HcmV?d00001 diff --git a/clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/lib-clap_macros-0156bd104f722f8d b/clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/lib-clap_macros-0156bd104f722f8d new file mode 100644 index 00000000000..3215f072bbc --- /dev/null +++ b/clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/lib-clap_macros-0156bd104f722f8d @@ -0,0 +1 @@ +bcd6e730bcaccf98 \ No newline at end of file diff --git a/clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/lib-clap_macros-0156bd104f722f8d.json b/clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/lib-clap_macros-0156bd104f722f8d.json new file mode 100644 index 00000000000..c21356fd7c8 --- /dev/null +++ b/clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/lib-clap_macros-0156bd104f722f8d.json @@ -0,0 +1 @@ +{"rustc":11877722514310538506,"target":4460363033944698956,"profile":14528549471338536373,"local":{"variant":"MtimeBased","fields":[[1485824906,736868657],[47,104,111,109,101,47,107,101,118,105,110,47,80,114,111,106,101,99,116,115,47,99,108,97,112,45,114,115,47,99,108,97,112,45,109,97,99,114,111,115,47,116,97,114,103,101,116,47,100,101,98,117,103,47,46,102,105,110,103,101,114,112,114,105,110,116,47,99,108,97,112,45,109,97,99,114,111,115,45,48,49,53,54,98,100,49,48,52,102,55,50,50,102,56,100,47,100,101,112,45,108,105,98,45,99,108,97,112,95,109,97,99,114,111,115,45,48,49,53,54,98,100,49,48,52,102,55,50,50,102,56,100]]},"features":"None","deps":[["quote v0.3.12",969197728868491966],["syn v0.11.4",16876859901146685650]],"rustflags":[]} \ No newline at end of file diff --git a/clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/dep-lib-quote-b64bd59975f476de b/clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/dep-lib-quote-b64bd59975f476de new file mode 100644 index 0000000000000000000000000000000000000000..31b1fdca0098536619889adb32460fbd20d0b737 GIT binary patch literal 1264 zcmeH`O%B2!5Jp}16kec=R;|)gcmq?0v7)xl zcq+R0Xn~>K0#jw2=RG(^sEl8QLu~4^mav! z`vG?LBeK!d`0(E(xu5MjzfZKiaHjvl;z!}Rut>{LJfz@j=ryy^xwmzQjJ}MwNDk-2 Bub2P; literal 0 HcmV?d00001 diff --git a/clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/lib-quote-b64bd59975f476de b/clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/lib-quote-b64bd59975f476de new file mode 100644 index 00000000000..d96f51d34bf --- /dev/null +++ b/clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/lib-quote-b64bd59975f476de @@ -0,0 +1 @@ +be62520a3348730d \ No newline at end of file diff --git a/clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/lib-quote-b64bd59975f476de.json b/clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/lib-quote-b64bd59975f476de.json new file mode 100644 index 00000000000..af075c20571 --- /dev/null +++ b/clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/lib-quote-b64bd59975f476de.json @@ -0,0 +1 @@ +{"rustc":11877722514310538506,"target":4005006414522881160,"profile":14528549471338536373,"local":{"variant":"Precalculated","fields":["0.3.12"]},"features":"None","deps":[],"rustflags":[]} \ No newline at end of file diff --git a/clap-macros/target/debug/.fingerprint/quote-c33138b2a30d7c05/dep-lib-quote b/clap-macros/target/debug/.fingerprint/quote-c33138b2a30d7c05/dep-lib-quote new file mode 100644 index 0000000000000000000000000000000000000000..688a0bac9ef62e6cec7eea7fb665c92fd3000b80 GIT binary patch literal 1264 zcmeH`OAdlC5QbfI3NJv%f>GfV-oU^xLgi7N787r86W0T zz3O1edP8UpV3Tb_zT9nwtj!2MECiczNfrvq`_i3T1C7dey|xst*Agk3%4|NLBE?d$ zdrHTTXo0R>15>GtToQ5JtP^6ukgAgq9Xg(JPqo1l$5fwiAil*9C&86jhb@mTX|b@HGDW{6u3q z7GHeRz4)-^lgT?ltsG*zygNzOxF_+?LgA&Y*$c(hwGPk0iB?x`>|;7*Z)qV_o@eQW zUoVLbAIj$~IHB;f5teK1yd8zJBx>!OYn71c;*^Q>8kqsyr5hxnoAhI7GK#h-_gBt7h|h=FsgB^B?yQ|R#u1$8{eA=|R6vg4+<52C z!#?X_`74WeB4ToQ5JtP^6ukgAq!B8dB3CfuDYyk}*-j*GUl$0XQdCvqTe5)#!_)Zh^AoM< zS$y$b58~>~CsXi(I%!k4yn9K`_!sfdLJ_2EI0(h{t%*aPH zsLaI=)$10VQT*8m$Bp*Ejlw$;jSlUt&dBs>&P2tzELB}Hy`SI25B8Mhd3KpfBQJ9v zNgPZMCbryJc3`Ls0gWUx_#lS>?o`Pj!ASNbErX#A59~B~FjgyuPid#oixiJMc-$&Z z<{iC8#)7-_Rua0&Fvcd6WSeq-8vrR4! zGED`CfpYnJKwyAaV5*Jtd_IhN543@1vfy6V?v;9Dknv7djdkJNC+{&!eTG>v-LjMxt8ES zN87=g-JURbz()2d-+L>Oou9ymgAgPe3ZO|1Cf43VW7W`Ebec9Nt5eLklxNHBa$c$x z Date: Mon, 30 Jan 2017 20:10:38 -0500 Subject: [PATCH 7/8] chore: adds clap-macros/target to ignore list --- .gitignore | 1 + clap-macros/.gitignore | 26 ------------------ clap-macros/target/debug/.cargo-lock | 0 .../dep-lib-clap_macros-0156bd104f722f8d | Bin 742 -> 0 bytes .../lib-clap_macros-0156bd104f722f8d | 1 - .../lib-clap_macros-0156bd104f722f8d.json | 1 - .../dep-lib-quote-b64bd59975f476de | Bin 1264 -> 0 bytes .../lib-quote-b64bd59975f476de | 1 - .../lib-quote-b64bd59975f476de.json | 1 - .../quote-c33138b2a30d7c05/dep-lib-quote | Bin 1264 -> 0 bytes .../quote-c33138b2a30d7c05/lib-quote | 1 - .../quote-c33138b2a30d7c05/lib-quote.json | 1 - .../syn-2cef086c5d2264c4/dep-lib-syn | Bin 3989 -> 0 bytes .../.fingerprint/syn-2cef086c5d2264c4/lib-syn | 1 - .../syn-2cef086c5d2264c4/lib-syn.json | 1 - .../dep-lib-syn-9d6699ed04adcefd | Bin 3989 -> 0 bytes .../lib-syn-9d6699ed04adcefd | 1 - .../lib-syn-9d6699ed04adcefd.json | 1 - .../dep-lib-unicode_xid-0718538d6479f922 | Bin 776 -> 0 bytes .../lib-unicode_xid-0718538d6479f922 | 1 - .../lib-unicode_xid-0718538d6479f922.json | 1 - .../dep-lib-unicode-xid | Bin 776 -> 0 bytes .../lib-unicode-xid | 1 - .../lib-unicode-xid.json | 1 - 24 files changed, 1 insertion(+), 40 deletions(-) delete mode 100644 clap-macros/.gitignore delete mode 100644 clap-macros/target/debug/.cargo-lock delete mode 100644 clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/dep-lib-clap_macros-0156bd104f722f8d delete mode 100644 clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/lib-clap_macros-0156bd104f722f8d delete mode 100644 clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/lib-clap_macros-0156bd104f722f8d.json delete mode 100644 clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/dep-lib-quote-b64bd59975f476de delete mode 100644 clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/lib-quote-b64bd59975f476de delete mode 100644 clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/lib-quote-b64bd59975f476de.json delete mode 100644 clap-macros/target/debug/.fingerprint/quote-c33138b2a30d7c05/dep-lib-quote delete mode 100644 clap-macros/target/debug/.fingerprint/quote-c33138b2a30d7c05/lib-quote delete mode 100644 clap-macros/target/debug/.fingerprint/quote-c33138b2a30d7c05/lib-quote.json delete mode 100644 clap-macros/target/debug/.fingerprint/syn-2cef086c5d2264c4/dep-lib-syn delete mode 100644 clap-macros/target/debug/.fingerprint/syn-2cef086c5d2264c4/lib-syn delete mode 100644 clap-macros/target/debug/.fingerprint/syn-2cef086c5d2264c4/lib-syn.json delete mode 100644 clap-macros/target/debug/.fingerprint/syn-9d6699ed04adcefd/dep-lib-syn-9d6699ed04adcefd delete mode 100644 clap-macros/target/debug/.fingerprint/syn-9d6699ed04adcefd/lib-syn-9d6699ed04adcefd delete mode 100644 clap-macros/target/debug/.fingerprint/syn-9d6699ed04adcefd/lib-syn-9d6699ed04adcefd.json delete mode 100644 clap-macros/target/debug/.fingerprint/unicode-xid-0718538d6479f922/dep-lib-unicode_xid-0718538d6479f922 delete mode 100644 clap-macros/target/debug/.fingerprint/unicode-xid-0718538d6479f922/lib-unicode_xid-0718538d6479f922 delete mode 100644 clap-macros/target/debug/.fingerprint/unicode-xid-0718538d6479f922/lib-unicode_xid-0718538d6479f922.json delete mode 100644 clap-macros/target/debug/.fingerprint/unicode-xid-13fc4e6b803f75ce/dep-lib-unicode-xid delete mode 100644 clap-macros/target/debug/.fingerprint/unicode-xid-13fc4e6b803f75ce/lib-unicode-xid delete mode 100644 clap-macros/target/debug/.fingerprint/unicode-xid-13fc4e6b803f75ce/lib-unicode-xid.json diff --git a/.gitignore b/.gitignore index 11101efcfb3..c590721c332 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ # Generated by Cargo /target/ /clap-test/target/ +/clap-macros/target/ # Cargo files Cargo.lock diff --git a/clap-macros/.gitignore b/clap-macros/.gitignore deleted file mode 100644 index 11101efcfb3..00000000000 --- a/clap-macros/.gitignore +++ /dev/null @@ -1,26 +0,0 @@ -# Compiled files -*.o -*.so -*.rlib -*.dll - -# Executables -*.exe - -# Generated by Cargo -/target/ -/clap-test/target/ - -# Cargo files -Cargo.lock - -# Temp files -.*~ - -# Backup files -*.bak -*.bk -*.orig - -# Project files -.vscode/* diff --git a/clap-macros/target/debug/.cargo-lock b/clap-macros/target/debug/.cargo-lock deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/dep-lib-clap_macros-0156bd104f722f8d b/clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/dep-lib-clap_macros-0156bd104f722f8d deleted file mode 100644 index 106e884850f0084f248c6a4d3f5898880d884908..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 742 zcmdUsO%8(~5QV$;6up3=rhoAiy#YZO)K-K5_Wac%im7Rubkz;a8y*9BAF8DQbcaXN zp{tfRpw=MT5*6!d7ldkQcIZnZvP4&45reX?hE*BVHsv@GyGe1H9Zx08(gQ!|Ilr*P z$YO6a9es&5uhLn)BG&4#HeT=sTIK}<-gJP8Qoh5r6nOZ~@Uo>f%-1HT=}Szd5Ts-C zQP9I1*Q4l%96pA?{wRY5RuPT$Ov}j$*CNJro$|n(S`nKJ@5ol(fX^~DXl_9(q9-v4 B58MC% diff --git a/clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/lib-clap_macros-0156bd104f722f8d b/clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/lib-clap_macros-0156bd104f722f8d deleted file mode 100644 index 3215f072bbc..00000000000 --- a/clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/lib-clap_macros-0156bd104f722f8d +++ /dev/null @@ -1 +0,0 @@ -bcd6e730bcaccf98 \ No newline at end of file diff --git a/clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/lib-clap_macros-0156bd104f722f8d.json b/clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/lib-clap_macros-0156bd104f722f8d.json deleted file mode 100644 index c21356fd7c8..00000000000 --- a/clap-macros/target/debug/.fingerprint/clap-macros-0156bd104f722f8d/lib-clap_macros-0156bd104f722f8d.json +++ /dev/null @@ -1 +0,0 @@ -{"rustc":11877722514310538506,"target":4460363033944698956,"profile":14528549471338536373,"local":{"variant":"MtimeBased","fields":[[1485824906,736868657],[47,104,111,109,101,47,107,101,118,105,110,47,80,114,111,106,101,99,116,115,47,99,108,97,112,45,114,115,47,99,108,97,112,45,109,97,99,114,111,115,47,116,97,114,103,101,116,47,100,101,98,117,103,47,46,102,105,110,103,101,114,112,114,105,110,116,47,99,108,97,112,45,109,97,99,114,111,115,45,48,49,53,54,98,100,49,48,52,102,55,50,50,102,56,100,47,100,101,112,45,108,105,98,45,99,108,97,112,95,109,97,99,114,111,115,45,48,49,53,54,98,100,49,48,52,102,55,50,50,102,56,100]]},"features":"None","deps":[["quote v0.3.12",969197728868491966],["syn v0.11.4",16876859901146685650]],"rustflags":[]} \ No newline at end of file diff --git a/clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/dep-lib-quote-b64bd59975f476de b/clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/dep-lib-quote-b64bd59975f476de deleted file mode 100644 index 31b1fdca0098536619889adb32460fbd20d0b737..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1264 zcmeH`O%B2!5Jp}16kec=R;|)gcmq?0v7)xl zcq+R0Xn~>K0#jw2=RG(^sEl8QLu~4^mav! z`vG?LBeK!d`0(E(xu5MjzfZKiaHjvl;z!}Rut>{LJfz@j=ryy^xwmzQjJ}MwNDk-2 Bub2P; diff --git a/clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/lib-quote-b64bd59975f476de b/clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/lib-quote-b64bd59975f476de deleted file mode 100644 index d96f51d34bf..00000000000 --- a/clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/lib-quote-b64bd59975f476de +++ /dev/null @@ -1 +0,0 @@ -be62520a3348730d \ No newline at end of file diff --git a/clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/lib-quote-b64bd59975f476de.json b/clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/lib-quote-b64bd59975f476de.json deleted file mode 100644 index af075c20571..00000000000 --- a/clap-macros/target/debug/.fingerprint/quote-b64bd59975f476de/lib-quote-b64bd59975f476de.json +++ /dev/null @@ -1 +0,0 @@ -{"rustc":11877722514310538506,"target":4005006414522881160,"profile":14528549471338536373,"local":{"variant":"Precalculated","fields":["0.3.12"]},"features":"None","deps":[],"rustflags":[]} \ No newline at end of file diff --git a/clap-macros/target/debug/.fingerprint/quote-c33138b2a30d7c05/dep-lib-quote b/clap-macros/target/debug/.fingerprint/quote-c33138b2a30d7c05/dep-lib-quote deleted file mode 100644 index 688a0bac9ef62e6cec7eea7fb665c92fd3000b80..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1264 zcmeH`OAdlC5QbfI3NJv%f>GfV-oU^xLgi7N787r86W0T zz3O1edP8UpV3Tb_zT9nwtj!2MECiczNfrvq`_i3T1C7dey|xst*Agk3%4|NLBE?d$ zdrHTTXo0R>15>GtToQ5JtP^6ukgAgq9Xg(JPqo1l$5fwiAil*9C&86jhb@mTX|b@HGDW{6u3q z7GHeRz4)-^lgT?ltsG*zygNzOxF_+?LgA&Y*$c(hwGPk0iB?x`>|;7*Z)qV_o@eQW zUoVLbAIj$~IHB;f5teK1yd8zJBx>!OYn71c;*^Q>8kqsyr5hxnoAhI7GK#h-_gBt7h|h=FsgB^B?yQ|R#u1$8{eA=|R6vg4+<52C z!#?X_`74WeB4ToQ5JtP^6ukgAq!B8dB3CfuDYyk}*-j*GUl$0XQdCvqTe5)#!_)Zh^AoM< zS$y$b58~>~CsXi(I%!k4yn9K`_!sfdLJ_2EI0(h{t%*aPH zsLaI=)$10VQT*8m$Bp*Ejlw$;jSlUt&dBs>&P2tzELB}Hy`SI25B8Mhd3KpfBQJ9v zNgPZMCbryJc3`Ls0gWUx_#lS>?o`Pj!ASNbErX#A59~B~FjgyuPid#oixiJMc-$&Z z<{iC8#)7-_Rua0&Fvcd6WSeq-8vrR4! zGED`CfpYnJKwyAaV5*Jtd_IhN543@1vfy6V?v;9Dknv7djdkJNC+{&!eTG>v-LjMxt8ES zN87=g-JURbz()2d-+L>Oou9ymgAgPe3ZO|1Cf43VW7W`Ebec9Nt5eLklxNHBa$c$x z Date: Mon, 30 Jan 2017 20:11:52 -0500 Subject: [PATCH 8/8] style(clap_macros): rustfmt run --- clap-macros/src/attr.rs | 64 +++++++++++-------- clap-macros/src/attrs.rs | 22 +++---- clap-macros/src/define_app.rs | 34 +++++----- clap-macros/src/define_sub_commands.rs | 4 +- clap-macros/src/field.rs | 14 ++-- clap-macros/src/from_arg_matches.rs | 14 ++-- .../src/sub_command_from_arg_matches.rs | 10 +-- 7 files changed, 85 insertions(+), 77 deletions(-) diff --git a/clap-macros/src/attr.rs b/clap-macros/src/attr.rs index d2acc524c4c..d4436cf5bd7 100644 --- a/clap-macros/src/attr.rs +++ b/clap-macros/src/attr.rs @@ -8,39 +8,40 @@ pub struct Attribute { impl Attribute { pub fn new(key: String) -> Attribute { - Attribute { key: key, values: vec![] } + Attribute { + key: key, + values: vec![], + } } - pub fn push(&mut self, value: syn::Lit) { - self.values.push(value) - } + pub fn push(&mut self, value: syn::Lit) { self.values.push(value) } pub fn values(&self) -> Vec { - self.values.iter().map(|s| match *s { - syn::Lit::Str(ref s, _) => s.clone(), - _ => panic!("clap-macros: multi-valued attributes must be strings"), - }).collect() + self.values + .iter() + .map(|s| match *s { + syn::Lit::Str(ref s, _) => s.clone(), + _ => panic!("clap-macros: multi-valued attributes must be strings"), + }) + .collect() } fn only_value(&self) -> &syn::Lit { if self.values.len() == 1 { &self.values[0] } else { - panic!("clap-macros: expected a single value for attribute '{}' but had multiple", self.key); + panic!("clap-macros: expected a single value for attribute '{}' but had multiple", + self.key); } } } impl<'a> Into for &'a Attribute { - fn into(self) -> syn::Lit { - self.only_value().clone() - } + fn into(self) -> syn::Lit { self.only_value().clone() } } impl<'a> Into<&'a syn::Lit> for &'a Attribute { - fn into(self) -> &'a syn::Lit { - self.only_value() - } + fn into(self) -> &'a syn::Lit { self.only_value() } } impl<'a> Into<&'a str> for &'a Attribute { @@ -48,7 +49,9 @@ impl<'a> Into<&'a str> for &'a Attribute { if let &syn::Lit::Str(ref value, _) = self.only_value() { value } else { - panic!("Expected string value for attribute {} but got a {:?}", self.key, self.only_value()); + panic!("Expected string value for attribute {} but got a {:?}", + self.key, + self.only_value()); } } } @@ -60,7 +63,9 @@ impl<'a> Into<&'a [u8]> for &'a Attribute { } else if let &syn::Lit::Str(ref value, _) = self.only_value() { value.as_bytes() } else { - panic!("Expected bytestring value for attribute {} but got a {:?}", self.key, self.only_value()); + panic!("Expected bytestring value for attribute {} but got a {:?}", + self.key, + self.only_value()); } } } @@ -72,7 +77,9 @@ impl<'a> Into for &'a Attribute { } else if let &syn::Lit::Str(ref value, _) = self.only_value() { value.parse().unwrap() } else { - panic!("Expected byte value for attribute {} but got a {:?}", self.key, self.only_value()); + panic!("Expected byte value for attribute {} but got a {:?}", + self.key, + self.only_value()); } } } @@ -85,7 +92,9 @@ impl<'a> Into for &'a Attribute { assert!(value.len() == 1); value.chars().next().unwrap() } else { - panic!("Expected char value for attribute {} but got a {:?}", self.key, self.only_value()); + panic!("Expected char value for attribute {} but got a {:?}", + self.key, + self.only_value()); } } } @@ -97,7 +106,9 @@ impl<'a> Into for &'a Attribute { } else if let &syn::Lit::Str(ref value, _) = self.only_value() { value.parse().unwrap() } else { - panic!("Expected int value for attribute {} but got a {:?}", self.key, self.only_value()); + panic!("Expected int value for attribute {} but got a {:?}", + self.key, + self.only_value()); } } } @@ -109,20 +120,17 @@ impl<'a> Into for &'a Attribute { } else if let &syn::Lit::Str(ref value, _) = self.only_value() { value.parse().unwrap() } else { - panic!("Expected bool value for attribute {} but got a {:?}", self.key, self.only_value()); + panic!("Expected bool value for attribute {} but got a {:?}", + self.key, + self.only_value()); } } } impl<'a> quote::ToTokens for &'a mut Attribute { - fn to_tokens(&self, tokens: &mut quote::Tokens) { - self.only_value().to_tokens(tokens) - } + fn to_tokens(&self, tokens: &mut quote::Tokens) { self.only_value().to_tokens(tokens) } } impl quote::ToTokens for Attribute { - fn to_tokens(&self, tokens: &mut quote::Tokens) { - self.only_value().to_tokens(tokens) - } + fn to_tokens(&self, tokens: &mut quote::Tokens) { self.only_value().to_tokens(tokens) } } - diff --git a/clap-macros/src/attrs.rs b/clap-macros/src/attrs.rs index 55e4e6c39be..2f1f498b911 100644 --- a/clap-macros/src/attrs.rs +++ b/clap-macros/src/attrs.rs @@ -30,8 +30,8 @@ impl Attributes { pub fn new() -> Self { Default::default() } pub fn from_attrs(attrs: &[syn::Attribute]) -> Self { - use syn::NestedMetaItem::*; - use syn::MetaItem::*; + use syn::NestedMetaItem as N; + use syn::MetaItem as M; let mut claps = BTreeMap::new(); for attr in attrs { if let syn::MetaItem::List(ref ident, ref values) = attr.value { @@ -41,25 +41,25 @@ impl Attributes { for value in values { match *value { // #[foo = "bar"] - MetaItem(NameValue(ref name, ref value)) => { + N::MetaItem(M::NameValue(ref name, ref value)) => { let &mut (_, ref mut attr) = claps.entry(name.to_string()) .or_insert((RefCell::new(0), Attribute::new(name.to_string()))); attr.push(value.clone()); } // #[foo] - MetaItem(Word(ref name)) => { + N::MetaItem(M::Word(ref name)) => { let &mut (_, ref mut attr) = claps.entry(name.to_string()) .or_insert((RefCell::new(0), Attribute::new(name.to_string()))); attr.push(syn::Lit::Bool(true)); } // #[derive(..)] - MetaItem(List(ref ident, ref values)) => { + N::MetaItem(M::List(ref ident, ref values)) => { let &mut (_, ref mut attr) = claps.entry(ident.as_ref().to_string()) .or_insert((RefCell::new(0), Attribute::new(ident.as_ref().to_string()))); for value in values { match *value { - MetaItem(Word(ref name)) => attr.push(name.as_ref().into()), + N::MetaItem(M::Word(ref name)) => attr.push(name.as_ref().into()), _ => { panic!("Invalid clap attribute {} literal value not supported", quote!(#attr).to_string().replace(" ", "")) @@ -157,12 +157,12 @@ impl FieldAttributes { /// Extracts all clap attributes of the form #[clap(i = V)] pub fn extract_attrs(ast: &syn::MacroInput) -> (Attributes, FieldAttributes) { - use syn::Body::*; - use syn::VariantData::*; + use syn::Body as B; + use syn::VariantData as V; let empty = Attributes::new(); let root_attrs = Attributes::from_attrs(&ast.attrs); let field_attrs = match ast.body { - Struct(Struct(ref fields)) => { + B::Struct(V::Struct(ref fields)) => { fields.iter() .map(|field| { (field.ident.clone().unwrap(), @@ -170,8 +170,8 @@ pub fn extract_attrs(ast: &syn::MacroInput) -> (Attributes, FieldAttributes) { }) .collect() } - Struct(Tuple(_)) => panic!("TODO: tuple struct unsupported msg"), - Struct(Unit) | Enum(_) => HashMap::new(), + B::Struct(V::Tuple(_)) => panic!("TODO: tuple struct unsupported msg"), + B::Struct(V::Unit) | B::Enum(_) => HashMap::new(), }; (root_attrs, FieldAttributes { diff --git a/clap-macros/src/define_app.rs b/clap-macros/src/define_app.rs index 89d64ad092a..d809cc2a605 100644 --- a/clap-macros/src/define_app.rs +++ b/clap-macros/src/define_app.rs @@ -1,8 +1,8 @@ use syn; use quote; -use attrs::{ Attributes, FieldAttributes }; -use field::{ Arg, Field, Subcommand }; +use attrs::{Attributes, FieldAttributes}; +use field::{Arg, Field, Subcommand}; fn expand_arg(arg: &Arg) -> quote::Tokens { let name = arg.name; @@ -47,7 +47,9 @@ fn expand_arg(arg: &Arg) -> quote::Tokens { } } -fn expand_args<'a, 'b: 'a, I>(args: I) -> quote::Tokens where I: Iterator> { +fn expand_args<'a, 'b: 'a, I>(args: I) -> quote::Tokens + where I: Iterator> +{ let args = args.map(expand_arg); quote! { .args(&[#(#args),*]) } } @@ -67,8 +69,9 @@ fn expand_subcommand(subcommand: &Subcommand) -> quote::Tokens { } fn expand_app(ast: &syn::MacroInput, attrs: &Attributes, fields: &[Field]) -> quote::Tokens { - let name = attrs.get("name").map(|a| a.into()) - .unwrap_or_else(|| syn::Lit::from(ast.ident.as_ref().to_lowercase())); + let name = attrs.get("name") + .map(|a| a.into()) + .unwrap_or_else(|| syn::Lit::from(ast.ident.as_ref().to_lowercase())); let version = if attrs.get_bool("crate_version") { Some(quote! { .version(crate_version!()) }) @@ -109,22 +112,21 @@ fn expand_app(ast: &syn::MacroInput, attrs: &Attributes, fields: &[Field]) -> qu } } -pub fn expand(ast: &syn::MacroInput, attrs: &Attributes, field_attrs: &FieldAttributes) -> quote::Tokens { +pub fn expand(ast: &syn::MacroInput, + attrs: &Attributes, + field_attrs: &FieldAttributes) + -> quote::Tokens { + use syn::Body as B; + use syn::VariantData as V; let fields = match ast.body { - syn::Body::Struct(syn::VariantData::Unit) => { - Vec::new() - } - syn::Body::Struct(syn::VariantData::Struct(ref fields)) => { + B::Struct(V::Unit) => Vec::new(), + B::Struct(V::Struct(ref fields)) => { fields.iter() .map(|field| Field::from((field, field_attrs.get(field)))) .collect() } - syn::Body::Struct(syn::VariantData::Tuple(_)) => { - panic!("#[derive(DefineApp)] is not supported on tuple structs") - } - syn::Body::Enum(_) => { - panic!("#[derive(DefineApp)] is not supported on enums") - } + B::Struct(V::Tuple(_)) => panic!("#[derive(DefineApp)] is not supported on tuple structs"), + B::Enum(_) => panic!("#[derive(DefineApp)] is not supported on enums"), }; let ident = &ast.ident; diff --git a/clap-macros/src/define_sub_commands.rs b/clap-macros/src/define_sub_commands.rs index e188bd97788..236b7326f69 100644 --- a/clap-macros/src/define_sub_commands.rs +++ b/clap-macros/src/define_sub_commands.rs @@ -29,9 +29,7 @@ pub fn expand(ast: &syn::MacroInput) -> quote::Tokens { }) .collect() } - syn::Body::Struct(_) => { - panic!("#[derive(DefineSubCommands)] is not supported on structs") - } + syn::Body::Struct(_) => panic!("#[derive(DefineSubCommands)] is not supported on structs"), }; let subcommands = expand_subcommands(&cmds); diff --git a/clap-macros/src/field.rs b/clap-macros/src/field.rs index 9e6a680c635..07146ca6280 100644 --- a/clap-macros/src/field.rs +++ b/clap-macros/src/field.rs @@ -63,8 +63,9 @@ impl<'a> From<(&'a syn::Field, &'a Attributes)> for Field<'a> { impl<'a> From<(&'a syn::Field, &'a Attributes)> for Arg<'a> { fn from((field, attrs): (&'a syn::Field, &'a Attributes)) -> Arg<'a> { - let name = attrs.get("name").map(|a| a.into()) - .unwrap_or_else(|| field.ident.as_ref().unwrap().as_ref()); + let name = attrs.get("name") + .map(|a| a.into()) + .unwrap_or_else(|| field.ident.as_ref().unwrap().as_ref()); let index = attrs.get("index").map(|a| a.into()); @@ -72,7 +73,8 @@ impl<'a> From<(&'a syn::Field, &'a Attributes)> for Arg<'a> { // telling us to not do so let is_flag = !index.is_some() && !attrs.get_bool("arg"); - let long = attrs.get("long").map(|a| a.into()) + let long = attrs.get("long") + .map(|a| a.into()) .or_else(|| if is_flag { Some(name) } else { None }); let short = attrs.get("short").map(|s| Into::::into(s).to_string()); @@ -87,7 +89,8 @@ impl<'a> From<(&'a syn::Field, &'a Attributes)> for Arg<'a> { is_optional = path.segments[0].ident == "Option"; is_vec = path.segments[0].ident == "Vec"; if is_optional || is_vec { - if let syn::PathParameters::AngleBracketed(ref params) = path.segments[0].parameters { + if let syn::PathParameters::AngleBracketed(ref params) = + path.segments[0].parameters { ty = ¶ms.types[0]; } else { panic!(); @@ -135,7 +138,8 @@ impl<'a> From<&'a syn::Field> for Subcommand<'a> { syn::Ty::Path(None, ref path) => { is_optional = path.segments[0].ident == "Option"; if is_optional { - if let syn::PathParameters::AngleBracketed(ref params) = path.segments[0].parameters { + if let syn::PathParameters::AngleBracketed(ref params) = + path.segments[0].parameters { ty = ¶ms.types[0]; } else { panic!(); diff --git a/clap-macros/src/from_arg_matches.rs b/clap-macros/src/from_arg_matches.rs index 3226e083b84..939fdd9c26e 100644 --- a/clap-macros/src/from_arg_matches.rs +++ b/clap-macros/src/from_arg_matches.rs @@ -2,7 +2,7 @@ use syn; use quote; use attrs::FieldAttributes; -use field::{ Arg, Field, Subcommand }; +use field::{Arg, Field, Subcommand}; fn expand_parse_arg(arg: &Arg, matches: &syn::Ident) -> quote::Tokens { let ident = arg.ident; @@ -83,20 +83,14 @@ fn expand_parse(ast: &syn::MacroInput, fields: &[Field], matches: &syn::Ident) - pub fn expand(ast: &syn::MacroInput, field_attrs: &FieldAttributes) -> quote::Tokens { let fields = match ast.body { - syn::Body::Struct(syn::VariantData::Unit) => { - Vec::new() - } + syn::Body::Struct(syn::VariantData::Unit) => Vec::new(), syn::Body::Struct(syn::VariantData::Struct(ref fields)) => { fields.iter() .map(|field| Field::from((field, field_attrs.get(field)))) .collect() } - syn::Body::Struct(syn::VariantData::Tuple(_)) => { - panic!("#[derive(FromArgMatches)] is not supported on tuple structs") - } - syn::Body::Enum(_) => { - panic!("#[derive(FromArgMatches)] is not supported on enums") - } + syn::Body::Struct(syn::VariantData::Tuple(_)) => panic!("#[derive(FromArgMatches)] is not supported on tuple structs"), + syn::Body::Enum(_) => panic!("#[derive(FromArgMatches)] is not supported on enums"), }; let ident = &ast.ident; diff --git a/clap-macros/src/sub_command_from_arg_matches.rs b/clap-macros/src/sub_command_from_arg_matches.rs index 78ef9ec23c3..7c7a4167b95 100644 --- a/clap-macros/src/sub_command_from_arg_matches.rs +++ b/clap-macros/src/sub_command_from_arg_matches.rs @@ -1,7 +1,11 @@ use syn; use quote; -fn expand_parse(me: &syn::Ident, cmds: &[(&syn::Ident, &syn::Ty)], name: &syn::Ident, matches: &syn::Ident) -> quote::Tokens { +fn expand_parse(me: &syn::Ident, + cmds: &[(&syn::Ident, &syn::Ty)], + name: &syn::Ident, + matches: &syn::Ident) + -> quote::Tokens { let variants = cmds.iter().map(|&(ident, ty)| { let name = ident.as_ref().to_lowercase(); quote! { #name => #me::#ident(<#ty as ::std::convert::From<&::clap::ArgMatches>>::from(#matches)) } @@ -39,9 +43,7 @@ pub fn expand(ast: &syn::MacroInput) -> quote::Tokens { }) .collect() } - syn::Body::Struct(_) => { - panic!("#[derive(SubCommandFromArgMatches)] is not supported on structs") - } + syn::Body::Struct(_) => panic!("#[derive(SubCommandFromArgMatches)] is not supported on structs"), }; let from = expand_parse(ident, &cmds, &name, &matches);