blob: 0dcb6d64272fd8fae0c4d9c230098031dbee643d [file] [log] [blame]
David Tolnay453cfd12016-10-23 11:00:14 -07001use super::{Attribute, AttrStyle, Body, Crate, Ident, Item, ItemKind, MacroInput, MetaItem};
David Tolnay9e24d4d2016-10-23 22:17:27 -07002use quote::Tokens;
David Tolnay453cfd12016-10-23 11:00:14 -07003
4use std::collections::BTreeMap as Map;
5use std::fs::File;
6use std::io::{Read, Write};
7use std::path::Path;
8
9/// Implementation of a custom derive. Custom derives take a struct or enum and
10/// expand it into zero or more items, typically `impl` items.
11pub trait CustomDerive {
12 /// Expand the given struct or enum. If this custom derive modifies the
13 /// input item or preserves it unmodified, it must be returned back in the
14 /// `original` field of Expanded. The custom derive may discard the input
15 /// item by setting `original` to None.
16 fn expand(&self, input: MacroInput) -> Result<Expanded, String>;
17}
18
19/// Produced by expanding a custom derive.
20pub struct Expanded {
21 /// The items (typically `impl` items) constructed by the custom derive.
22 pub new_items: Vec<Item>,
23 /// The input to the custom derive, whether modified or unmodified. If the
24 /// custom derive discards the input item it may do so by setting `original`
25 /// to None.
26 pub original: Option<MacroInput>,
27}
28
29/// Registry of custom derives. Callers add custom derives to a registry, then
30/// use the registry to expand those derives in a source file.
31#[derive(Default)]
32pub struct Registry<'a> {
33 derives: Map<String, Box<CustomDerive + 'a>>,
34}
35
36impl<T> CustomDerive for T
37 where T: Fn(MacroInput) -> Result<Expanded, String>
38{
39 fn expand(&self, input: MacroInput) -> Result<Expanded, String> {
40 self(input)
41 }
42}
43
44impl<'a> Registry<'a> {
45 pub fn new() -> Self {
46 Default::default()
47 }
48
49 /// Register a custom derive. A `fn(MacroInput) -> Result<Expanded, String>`
50 /// may be used as a custom derive.
51 ///
52 /// ```ignore
53 /// registry.add_derive("Serialize", expand_serialize);
54 /// ```
55 pub fn add_derive<T>(&mut self, name: &str, derive: T)
56 where T: CustomDerive + 'a
57 {
58 self.derives.insert(name.into(), Box::new(derive));
59 }
60
61 /// Read Rust source code from the `src` file, expand the custom derives
62 /// that have been registered, and write the result to the `dst` file.
63 pub fn expand_file<S, D>(&self, src: S, dst: D) -> Result<(), String>
64 where S: AsRef<Path>,
65 D: AsRef<Path>
66 {
67 // Open the src file
68 let mut src = match File::open(src) {
69 Ok(open) => open,
70 Err(err) => return Err(err.to_string()),
71 };
72
73 // Read the contents of the src file to a String
74 let mut content = String::new();
75 if let Err(err) = src.read_to_string(&mut content) {
76 return Err(err.to_string());
77 }
78
79 // Parse the contents
80 let krate = try!(super::parse_crate(&content));
81
82 // Expand
83 let expanded = try!(expand_crate(self, krate));
84
85 // Print the expanded code to a String
David Tolnay9e24d4d2016-10-23 22:17:27 -070086 let out = try!(pretty(quote!(#expanded)));
David Tolnay453cfd12016-10-23 11:00:14 -070087
88 // Create or truncate the dst file, opening in write-only mode
89 let mut dst = match File::create(dst) {
90 Ok(create) => create,
91 Err(err) => return Err(err.to_string()),
92 };
93
94 // Write expanded code to the dst file
95 if let Err(err) = dst.write_all(out.as_bytes()) {
96 return Err(err.to_string());
97 }
98
99 Ok(())
100 }
101}
102
103fn expand_crate(reg: &Registry, krate: Crate) -> Result<Crate, String> {
104 let mut items = Vec::new();
105 for item in krate.items {
106 try!(expand_item(reg, item, Vec::new(), &mut items));
107 }
108 Ok(Crate { items: items, ..krate })
109}
110
David Tolnaye8cb5f42016-10-23 22:26:20 -0700111fn expand_item(reg: &Registry,
112 mut item: Item,
113 cfg: Vec<MetaItem>,
114 out: &mut Vec<Item>)
115 -> Result<(), String> {
David Tolnay453cfd12016-10-23 11:00:14 -0700116 let (body, generics) = match item.node {
117 ItemKind::Enum(variants, generics) => (Body::Enum(variants), generics),
118 ItemKind::Struct(variant_data, generics) => (Body::Struct(variant_data), generics),
119 _ => {
120 // Custom derives cannot apply to this item, preserve it unmodified
121 item.attrs.extend(combine_cfgs(cfg));
122 out.push(item);
123 return Ok(());
124 }
125 };
126 let macro_input = MacroInput {
127 ident: item.ident,
128 vis: item.vis,
129 attrs: item.attrs,
130 generics: generics,
131 body: body,
132 };
133 expand_macro_input(reg, macro_input, cfg, out)
134}
135
136fn expand_macro_input(reg: &Registry,
137 mut input: MacroInput,
138 inherited_cfg: Vec<MetaItem>,
139 out: &mut Vec<Item>)
140 -> Result<(), String> {
141 let mut derives = Vec::new();
142 let mut all_cfg = inherited_cfg;
143
144 // Find custom derives on this item, removing them from the input
145 input.attrs = input.attrs
146 .into_iter()
147 .flat_map(|attr| {
148 let (new_derives, cfg, attr) = parse_attr(reg, attr);
149 derives.extend(new_derives);
150 all_cfg.extend(cfg);
151 attr
152 })
153 .collect();
154
155 // Expand each custom derive
156 for derive in derives {
157 let expanded = try!(reg.derives[derive.name.as_ref()].expand(input));
158
159 for new_item in expanded.new_items {
160 let mut extended_cfg = all_cfg.clone();
161 extended_cfg.extend(derive.cfg.clone());
162 try!(expand_item(reg, new_item, extended_cfg, out));
163 }
164
165 input = match expanded.original {
166 Some(input) => input,
167 None => return Ok(()),
168 };
169 }
170
171 input.attrs.extend(combine_cfgs(all_cfg));
172 out.push(input.into());
173 Ok(())
174}
175
176struct Derive {
177 name: Ident,
178 /// If the custom derive was behind a cfg_attr
179 cfg: Option<MetaItem>,
180}
181
182/// Pull custom derives and cfgs out of the given Attribute.
183fn parse_attr(reg: &Registry, attr: Attribute) -> (Vec<Derive>, Vec<MetaItem>, Option<Attribute>) {
184 if attr.style != AttrStyle::Outer || attr.is_sugared_doc {
185 return (Vec::new(), Vec::new(), Some(attr));
186 }
187
188 let (name, nested) = match attr.value {
189 MetaItem::List(name, nested) => (name, nested),
190 _ => return (Vec::new(), Vec::new(), Some(attr)),
191 };
192
193 match name.as_ref() {
194 "derive" => {
195 let (derives, attr) = parse_derive_attr(reg, nested);
196 let derives = derives.into_iter()
197 .map(|d| {
198 Derive {
199 name: d,
200 cfg: None,
201 }
202 })
203 .collect();
204 (derives, Vec::new(), attr)
205 }
206 "cfg_attr" => {
207 let (derives, attr) = parse_cfg_attr(reg, nested);
208 (derives, Vec::new(), attr)
209 }
210 "cfg" => (Vec::new(), nested, None),
211 _ => {
212 // Rebuild the original attribute because it was destructured above
213 let attr = Attribute {
214 style: AttrStyle::Outer,
215 value: MetaItem::List(name, nested),
216 is_sugared_doc: false,
217 };
218 (Vec::new(), Vec::new(), Some(attr))
219 }
220 }
221}
222
223/// Assuming the given nested meta-items came from a #[derive(...)] attribute,
224/// pull out the ones that are custom derives.
225fn parse_derive_attr(reg: &Registry, nested: Vec<MetaItem>) -> (Vec<Ident>, Option<Attribute>) {
226 let mut derives = Vec::new();
227
228 let remaining: Vec<_> = nested.into_iter()
229 .flat_map(|meta| {
230 let word = match meta {
231 MetaItem::Word(word) => word,
232 _ => return Some(meta),
233 };
234 if reg.derives.contains_key(word.as_ref()) {
235 derives.push(word);
236 None
237 } else {
238 Some(MetaItem::Word(word))
239 }
240 })
241 .collect();
242
243 let attr = if remaining.is_empty() {
244 // Elide an empty #[derive()]
245 None
246 } else {
247 Some(Attribute {
248 style: AttrStyle::Outer,
249 value: MetaItem::List("derive".into(), remaining),
250 is_sugared_doc: false,
251 })
252 };
253
254 (derives, attr)
255}
256
257/// Assuming the given nested meta-items came from a #[cfg_attr(...)] attribute,
258/// pull out any custom derives contained within.
259fn parse_cfg_attr(reg: &Registry, nested: Vec<MetaItem>) -> (Vec<Derive>, Option<Attribute>) {
260 if nested.len() != 2 {
261 let attr = Attribute {
262 style: AttrStyle::Outer,
263 value: MetaItem::List("cfg_attr".into(), nested),
264 is_sugared_doc: false,
265 };
266 return (Vec::new(), Some(attr));
267 }
268
269 let mut iter = nested.into_iter();
270 let cfg = iter.next().unwrap();
271 let arg = iter.next().unwrap();
272
273 let (name, nested) = match arg {
274 MetaItem::List(name, nested) => (name, nested),
275 _ => {
276 let attr = Attribute {
277 style: AttrStyle::Outer,
278 value: MetaItem::List("cfg_attr".into(), vec![cfg, arg]),
279 is_sugared_doc: false,
280 };
281 return (Vec::new(), Some(attr));
282 }
283 };
284
285 if name == "derive" {
286 let (derives, attr) = parse_derive_attr(reg, nested);
287 let derives = derives.into_iter()
288 .map(|d| {
289 Derive {
290 name: d,
291 cfg: Some(cfg.clone()),
292 }
293 })
294 .collect();
295 let attr = attr.map(|attr| {
296 Attribute {
297 style: AttrStyle::Outer,
298 value: MetaItem::List("cfg_attr".into(), vec![cfg, attr.value]),
299 is_sugared_doc: false,
300 }
301 });
302 (derives, attr)
303 } else {
304 let attr = Attribute {
305 style: AttrStyle::Outer,
306 value: MetaItem::List("cfg_attr".into(), vec![cfg, MetaItem::List(name, nested)]),
307 is_sugared_doc: false,
308 };
309 (Vec::new(), Some(attr))
310 }
311}
312
313/// Combine a list of cfg expressions into an attribute like `#[cfg(a)]` or
314/// `#[cfg(all(a, b, c))]`, or nothing if there are no cfg expressions.
315fn combine_cfgs(cfg: Vec<MetaItem>) -> Option<Attribute> {
316 // Flatten `all` cfgs so we don't nest `all` inside of `all`.
David Tolnaye8cb5f42016-10-23 22:26:20 -0700317 let cfg: Vec<_> = cfg.into_iter()
318 .flat_map(|cfg| {
319 let (name, nested) = match cfg {
320 MetaItem::List(name, nested) => (name, nested),
321 _ => return vec![cfg],
322 };
323 if name == "all" {
324 nested
325 } else {
326 vec![MetaItem::List(name, nested)]
327 }
328 })
329 .collect();
David Tolnay453cfd12016-10-23 11:00:14 -0700330
331 let value = match cfg.len() {
332 0 => return None,
333 1 => cfg,
334 _ => vec![MetaItem::List("all".into(), cfg)],
335 };
336
337 Some(Attribute {
338 style: AttrStyle::Outer,
339 value: MetaItem::List("cfg".into(), value),
340 is_sugared_doc: false,
341 })
342}
David Tolnay9e24d4d2016-10-23 22:17:27 -0700343
344#[cfg(not(feature = "pretty"))]
345fn pretty(tokens: Tokens) -> Result<String, String> {
346 Ok(tokens.to_string())
347}
348
349#[cfg(feature = "pretty")]
350fn pretty(tokens: Tokens) -> Result<String, String> {
351 use syntax::parse::{self, ParseSess};
352 use syntax::print::pprust;
353
354 let name = "syn".to_string();
355 let source = tokens.to_string();
356 let cfg = Vec::new();
357 let sess = ParseSess::new();
358 let krate = match parse::parse_crate_from_source_str(name, source, cfg, &sess) {
359 Ok(krate) => krate,
360 Err(mut err) => {
361 err.emit();
362 return Err("pretty printer failed to parse expanded code".into());
363 }
364 };
365
366 if sess.span_diagnostic.has_errors() {
367 return Err("pretty printer failed to parse expanded code".into());
368 }
369
370 let mut reader = &tokens.to_string().into_bytes()[..];
371 let mut writer = Vec::new();
372 let ann = pprust::NoAnn;
373
374 try!(pprust::print_crate(
375 sess.codemap(),
376 &sess.span_diagnostic,
377 &krate,
378 "".to_string(),
379 &mut reader,
380 Box::new(&mut writer),
381 &ann,
382 false).map_err(|err| err.to_string()));
383
384 String::from_utf8(writer).map_err(|err| err.to_string())
385}