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}