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}