Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Minor changes and rustfmt #2

Merged
merged 5 commits into from
Jan 31, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# Generated by Cargo
/target/
/clap-test/target/
/clap-macros/target/

# Cargo files
Cargo.lock
Expand Down
64 changes: 36 additions & 28 deletions clap-macros/src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,47 +8,50 @@ 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<String> {
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<syn::Lit> 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 {
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());
panic!("Expected string value for attribute {} but got a {:?}",
self.key,
self.only_value());
}
}
}
Expand All @@ -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());
}
}
}
Expand All @@ -72,7 +77,9 @@ impl<'a> Into<u8> 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());
}
}
}
Expand All @@ -85,7 +92,9 @@ impl<'a> Into<char> 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());
}
}
}
Expand All @@ -97,7 +106,9 @@ impl<'a> Into<u64> 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());
}
}
}
Expand All @@ -109,20 +120,17 @@ impl<'a> Into<bool> 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) }
}

206 changes: 117 additions & 89 deletions clap-macros/src/attrs.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::cell::RefCell;
use std::collections::{ BTreeMap, HashMap };
use std::collections::{BTreeMap, HashMap};

use syn;

Expand All @@ -16,15 +16,106 @@ pub struct FieldAttributes {
map: HashMap<syn::Ident, (RefCell<usize>, 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 from_attrs(attrs: &[syn::Attribute]) -> Self {
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 {
if ident != "clap" {
panic!("Attribute other than #[clap(..)] found");
}
for value in values {
match *value {
// #[foo = "bar"]
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]
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(..)]
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 {
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(" ", ""))
}
}
}
}
_ => {
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 {
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)
}
}
}
}
Expand All @@ -39,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()));
}
Expand All @@ -64,90 +155,27 @@ impl FieldAttributes {
}
}

fn extract_attrs_inner(attrs: &Vec<syn::Attribute>) -> 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);
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 {
syn::Body::Struct(syn::VariantData::Struct(ref fields)) => {
fields
.iter()
.map(|field| (field.ident.clone().unwrap(), (RefCell::new(0), extract_attrs_inner(&field.attrs))))
B::Struct(V::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()
}
B::Struct(V::Tuple(_)) => panic!("TODO: tuple struct unsupported msg"),
B::Struct(V::Unit) | B::Enum(_) => HashMap::new(),
};
(root_attrs, FieldAttributes { empty: empty, map: field_attrs })
(root_attrs,
FieldAttributes {
empty: empty,
map: field_attrs,
})
}
Loading