macro_input_macros/
lib.rs

1mod convert;
2mod fielddef;
3mod fns;
4mod input;
5mod lint;
6
7use heck::{ShoutySnekCase, SnekCase};
8use input::{DEFAULT_VALUE_FIELD, RENAME_FIELD};
9use macro_compose::{Collector, Context};
10use proc_macro::TokenStream;
11use proc_macro2::Ident;
12use quote::format_ident;
13use syn::{DeriveInput, Field, LitStr};
14
15#[proc_macro_derive(MacroInput, attributes(macro_input))]
16/// automatically derive `TryFrom<&[syn::Attribute]>` and `fn strip(attrs: &mut Vec<syn::Attribute>)`
17///
18/// supported types:
19/// - `u8`, `i32`, `f32`, `char`, `bool`, `String` or `Vec<u8>` for parsing literals eg `#[foo(bar = 3)]`
20/// - `Option<u8>`, `Option<i32>`, `Option<f32>`, `Option<char>`, `Option<bool>`, `Option<String>` or `Option<Vec<u8>>` for optionally parsing literals eg either `#[foo(bar = 3)]` or nothing
21/// - `Option<()>` for parsing flags eg `#[foo(bar)]`
22/// - `Vec<Ident>` and `Option<Vec<Ident>>` for parsing idents eg `#[foo(bar(baz, qux))]`
23///
24/// paths get converted to lower_snake unless `rename` is specified
25///
26/// use `#[macro_input]` for customization:
27/// - `rename` to rename either the path or field name eg `#[macro_input(rename = "some_name")]`
28/// - `default_value` for default values eg `#[macro_input(default_value = "some literal")]` (this is not supported for idents)
29/// # Example
30/// ```
31/// use macro_input_macros::MacroInput;
32/// use std::convert::TryFrom;
33/// use syn::{parse_quote, Attribute};
34///
35/// #[derive(MacroInput, PartialEq, Debug)]
36/// pub struct SomeInput {
37///     pub flag: Option<()>,
38///     pub optional: Option<i32>,
39///     #[macro_input(default_value = 3)]
40///     pub with_default: i32,
41///     pub required: i32,
42/// }
43///
44/// #[derive(MacroInput, PartialEq, Debug)]
45/// #[macro_input(rename = "YetAnotherName")]
46/// pub struct OtherInput {
47///     #[macro_input(rename = "new_name")]
48///     pub renamed: i32,
49/// }
50///
51/// # fn main() -> syn::Result<()> {
52/// // construct some attributes
53/// let attr1: Attribute = parse_quote!(#[some_input(flag, required = 5)]);
54/// let attr2: Attribute = parse_quote!(#[some_input(optional = 8, with_default = 4)]);
55/// let attr3: Attribute = parse_quote!(#[YetAnotherName(new_name = 6)]);
56///
57/// // parse SomeInput with only some attributes
58/// let input1 = SomeInput::try_from(&[attr1.clone()] as &[Attribute])?;
59/// // parse SomeInput with all attributes
60/// let input2 = SomeInput::try_from(&[attr1, attr2] as &[Attribute])?;
61/// // parse OtherInput
62/// let input3 = OtherInput::try_from(&[attr3] as &[Attribute])?;
63///
64/// assert_eq!(input1, SomeInput { flag: Some(()), optional: None, with_default: 3, required: 5 });
65/// assert_eq!(input2, SomeInput { flag: Some(()), optional: Some(8), with_default: 4, required: 5 });
66/// assert_eq!(input3, OtherInput { renamed: 6 });
67/// # Ok(())
68/// # }
69/// ```
70
71pub fn derive_macro_input(item: TokenStream) -> TokenStream {
72    let mut collector = Collector::new();
73    let mut ctx = Context::<DeriveInput>::new_parse(&mut collector, item);
74
75    // lint
76    ctx.lint(&input::STRUCT_LINT);
77    ctx.lint(&fielddef::Name);
78    ctx.lint(&fielddef::FieldType);
79    ctx.lint(&lint::Name);
80
81    // expand
82    ctx.expand(&convert::TryFromAttributes);
83    ctx.expand(&fielddef::ConstFields);
84    ctx.expand(&fns::Strip);
85
86    collector.finish().into()
87}
88
89fn mod_name(input: &DeriveInput) -> Ident {
90    let path = input.ident.to_string().to_snek_case();
91    format_ident!("__{}", &*path, span = input.ident.span())
92}
93
94fn field_name(f: &Field) -> (String, Ident) {
95    let (name, span) = RENAME_FIELD.get::<LitStr>(&f.attrs).unwrap().map_or_else(
96        || {
97            let ident = f.ident.as_ref().unwrap();
98            (ident.to_string(), ident.span())
99        },
100        |s| (s.value(), s.span()),
101    );
102    let field_name = format!("{}_field", name).TO_SHOUTY_SNEK_CASE();
103    (name, Ident::new(&*field_name, span))
104}