blanket/
lib.rs

1#![cfg_attr(feature = "_doc", feature(doc_cfg, external_doc))]
2#![cfg_attr(feature = "_doc", doc(include = "../README.md"))]
3
4extern crate proc_macro;
5extern crate proc_macro2;
6extern crate quote;
7
8use std::collections::HashSet;
9
10use quote::{quote, ToTokens};
11use syn::{parse_macro_input, punctuated::Punctuated, spanned::Spanned};
12
13// ---------------------------------------------------------------------------
14
15mod default;
16mod derive;
17mod utils;
18
19// ---------------------------------------------------------------------------
20
21struct Args {
22    default: Option<syn::Path>,
23    derives: HashSet<derive::Derive>,
24}
25
26impl Args {
27    fn from_meta(arg: &syn::Meta) -> syn::Result<Self> {
28        let mut default = None;
29        let mut derives = HashSet::new();
30
31        match arg {
32            syn::Meta::List(ref l) if l.path.to_token_stream().to_string() == "derive" => {
33                let types = l.parse_args_with(
34                    Punctuated::<syn::Path, syn::Token![,]>::parse_separated_nonempty,
35                )?;
36                for pair in types.into_pairs() {
37                    if let Some(d) = derive::Derive::from_path(pair.value()) {
38                        derives.insert(d);
39                    } else {
40                        return Err(syn::Error::new(
41                            pair.span(),
42                            "unknown blanket derive option",
43                        ));
44                    }
45                }
46            }
47            syn::Meta::NameValue(ref n) if n.path.to_token_stream().to_string() == "default" => {
48                match n.value {
49                    syn::Expr::Lit(ref lit) => {
50                        if let syn::Lit::Str(ref s) = lit.lit {
51                            match syn::parse_str(&s.value()) {
52                                Ok(path) if default.is_none() => {
53                                    default = Some(path);
54                                }
55                                Ok(_) => {
56                                    return Err(syn::Error::new(
57                                        s.span(),
58                                        "duplicate default module given",
59                                    ))
60                                }
61                                Err(_) => {
62                                    return Err(syn::Error::new(
63                                        s.span(),
64                                        "expected module identifier",
65                                    ))
66                                }
67                            }
68                        } else {
69                            return Err(syn::Error::new(lit.lit.span(), "expected string literal"));
70                        }
71                    }
72                    syn::Expr::Path(ref expr) => {
73                        if default.replace(expr.path.clone()).is_some() {
74                            return Err(syn::Error::new(
75                                n.span(),
76                                "duplicate default module given",
77                            ));
78                        }
79                    }
80                    _ => {
81                        return Err(syn::Error::new(
82                            n.value.span(),
83                            "expected path or string literal",
84                        ));
85                    }
86                }
87            }
88            _ => return Err(syn::Error::new(arg.span(), "unexpected argument")),
89        }
90
91        Ok(Self { default, derives })
92    }
93}
94
95// ---------------------------------------------------------------------------
96
97#[proc_macro_attribute]
98pub fn blanket(
99    args: proc_macro::TokenStream,
100    input: proc_macro::TokenStream,
101) -> proc_macro::TokenStream {
102    // parse input
103    let trait_ = parse_macro_input!(input as syn::ItemTrait);
104    let args = parse_macro_input!(args as syn::Meta);
105    // parse macro arguments and immediately exit if they are invalid
106    let args = match Args::from_meta(&args) {
107        Ok(args) => args,
108        Err(e) => {
109            let err = e.to_compile_error();
110            return proc_macro::TokenStream::from(quote!(#err #trait_));
111        }
112    };
113    // generate output
114    let mut out = proc_macro2::TokenStream::new();
115    // update trait methods declaration if given a `default = "..."` argument,
116    // otherwise simply keep the output
117    match args.default {
118        None => out.extend(quote!(#trait_)),
119        Some(d) => match default::defer_trait_methods(trait_.clone(), d) {
120            Ok(trait_) => out.extend(quote!(#trait_)),
121            Err(err) => out.extend(err.to_compile_error()),
122        },
123    };
124    // add derived implementations
125    for d in args.derives {
126        match d.defer_trait_methods(&trait_) {
127            Ok(item) => out.extend(quote!(#item)),
128            Err(e) => out.extend(e.to_compile_error()),
129        }
130    }
131    // // return the new `proc-macro2` token stream as a `proc-macro` stream
132    proc_macro::TokenStream::from(out)
133}