wrapped_vec/
lib.rs

1extern crate proc_macro;
2
3use quote::quote;
4
5use proc_macro2::{Span, TokenStream};
6use syn::{parse_macro_input, DeriveInput, Ident};
7
8struct Idents {
9    item: Ident,
10    collection: Ident,
11    derives: Option<Vec<Ident>>,
12}
13
14impl Idents {
15    fn new(input: &DeriveInput) -> Result<Idents, String> {
16        let collection_name =
17            attr_string_val(input, "CollectionName").expect("Need [CollectionName=\"...\"]");
18        let derives = Idents::parse_derives(input);
19
20        Ok(Idents {
21            item: input.ident.clone(),
22            collection: Ident::new(&collection_name, Span::call_site()),
23            derives: derives,
24        })
25    }
26
27    fn parse_derives(input: &DeriveInput) -> Option<Vec<Ident>> {
28        match attr_string_val(input, "CollectionDerives") {
29            Some(derives_str) => {
30                if derives_str.is_empty() {
31                    return None;
32                }
33
34                Some(
35                    derives_str
36                        .split(",")
37                        .map(|s| Ident::new(s.trim(), Span::call_site()))
38                        .collect(),
39                )
40            }
41            None => None,
42        }
43    }
44
45    fn as_parts(&self) -> (&Ident, &Ident, &Option<Vec<Ident>>) {
46        (&self.item, &self.collection, &self.derives)
47    }
48}
49
50struct Docs {
51    wrapper: String,
52    new: String,
53    is_empty: String,
54    len: String,
55    iter: String,
56}
57
58macro_rules! doc_attr {
59    ($input:ident, $attr:expr, $default:expr) => {
60        attr_string_val($input, $attr).unwrap_or($default);
61    };
62}
63
64impl Docs {
65    fn new(input: &DeriveInput, idents: &Idents) -> Docs {
66        let wrapper = doc_attr!(
67            input,
68            "CollectionDoc",
69            format!("A collection of {}s", idents.item)
70        );
71        let new = doc_attr!(
72            input,
73            "CollectionNewDoc",
74            format!("Creates a new, empty {}", idents.collection)
75        );
76        let is_empty = doc_attr!(
77            input,
78            "CollectionIsEmptyDoc",
79            format!(
80                "Returns true if the {} contains no {}s",
81                idents.collection, idents.item
82            )
83        );
84        let len = doc_attr!(
85            input,
86            "CollectionLenDoc",
87            format!(
88                "Returns the number of {}s in the {}",
89                idents.item, idents.collection
90            )
91        );
92        let iter = doc_attr!(
93            input,
94            "CollectionIterDoc",
95            format!("Returns an iterator over the {}", idents.collection)
96        );
97
98        Docs {
99            wrapper: wrapper,
100            new: new,
101            is_empty: is_empty,
102            len: len,
103            iter: iter,
104        }
105    }
106
107    fn as_parts(&self) -> (&String, &String, &String, &String, &String) {
108        (
109            &self.wrapper,
110            &self.new,
111            &self.is_empty,
112            &self.len,
113            &self.iter,
114        )
115    }
116}
117
118#[proc_macro_derive(
119    WrappedVec,
120    attributes(
121        CollectionName,
122        CollectionDerives,
123        CollectionDoc,
124        CollectionNewDoc,
125        CollectionIsEmptyDoc,
126        CollectionLenDoc,
127        CollectionIterDoc
128    )
129)]
130pub fn wrapped_vec(token_stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
131    let input = parse_macro_input!(token_stream as DeriveInput);
132    impl_wrapped_vec(&input).unwrap().into()
133}
134
135fn impl_wrapped_vec(input: &DeriveInput) -> Result<TokenStream, String> {
136    let idents = Idents::new(input)?;
137    let docs = Docs::new(input, &idents);
138    Ok(generate_wrapped_vec(&idents, &docs))
139}
140
141fn generate_wrapped_vec(idents: &Idents, docs: &Docs) -> TokenStream {
142    let (item_ident, collection_ident, collection_derive) = idents.as_parts();
143    let (collection_doc, new_doc, is_empty_doc, len_doc, iter_doc) = docs.as_parts();
144
145    let derives_toks = match collection_derive.clone() {
146        Some(derives) => {
147            quote! { #[derive(#(#derives),*)] }
148        }
149        None => {
150            quote! {}
151        }
152    };
153
154    quote! {
155        #[doc=#collection_doc]
156        #derives_toks
157        pub struct #collection_ident(Vec<#item_ident>);
158
159        impl ::std::iter::FromIterator<#item_ident> for #collection_ident {
160            fn from_iter<I: IntoIterator<Item=#item_ident>>(iter: I) -> Self {
161                let mut inner = vec![];
162                inner.extend(iter);
163                #collection_ident(inner)
164            }
165        }
166
167        impl From<Vec<#item_ident>> for #collection_ident {
168            fn from(ids: Vec<#item_ident>) -> #collection_ident {
169                let mut new = #collection_ident::new();
170                new.extend(ids);
171                new
172            }
173        }
174
175        impl IntoIterator for #collection_ident {
176            type Item = #item_ident;
177            type IntoIter = ::std::vec::IntoIter<#item_ident>;
178
179            fn into_iter(self) -> Self::IntoIter {
180                self.0.into_iter()
181            }
182        }
183
184        impl<'a> IntoIterator for &'a #collection_ident {
185            type Item = &'a #item_ident;
186            type IntoIter = ::std::slice::Iter<'a, #item_ident>;
187
188            fn into_iter(self) -> Self::IntoIter {
189                self.0.iter()
190            }
191        }
192
193        impl Extend<#item_ident> for #collection_ident {
194            fn extend<T: IntoIterator<Item=#item_ident>>(&mut self, iter: T) {
195                self.0.extend(iter);
196            }
197        }
198
199        impl #collection_ident {
200
201            #[doc=#new_doc]
202            pub fn new() -> #collection_ident {
203                #collection_ident(vec![])
204            }
205
206            #[doc=#is_empty_doc]
207            pub fn is_empty(&self) -> bool {
208                self.0.is_empty()
209            }
210
211            #[doc=#len_doc]
212            pub fn len(&self) -> usize {
213                self.0.len()
214            }
215
216            #[doc=#iter_doc]
217            pub fn iter<'a>(&'a self) -> ::std::slice::Iter<'a, #item_ident> {
218                self.into_iter()
219            }
220
221        }
222    }
223}
224
225fn attr_string_val(input: &DeriveInput, attr_name: &'static str) -> Option<String> {
226    input.attrs.iter().find_map(|input| {
227        if let Ok(attribute) = input.parse_meta() {
228            if let syn::Meta::NameValue(name_value) = attribute {
229                if name_value.path.is_ident(attr_name) {
230                    if let syn::Lit::Str(s) = &name_value.lit {
231                        return Some(s.value());
232                    }
233                }
234            }
235        }
236        None
237    })
238}