Derive Macro synthez_codegen::ParseAttrs[][src]

#[derive(ParseAttrs)]
{
    // Attributes available to this derive:
    #[parse]
}
Expand description

Deriving of synthez::ParseAttrs along with a syn::parse::Parse implementation to parse syn::Attributes into a custom defined struct.

Field requirements

Each field should be wrapped into a field::Container implementor, which describes and influes the parsing logic. Use Required field::Container in case your parsing logic demands mandatory specifying of a value.

Type of the parsed valued (the one contained in a field::Container) must implement Parse and Spanned (vital for compile-time error reporting). You may use the Spanning wrapper in case it doesn’t implement the latest.

Arguments

ident, value, map or nested (mandatory)

Defines kind of parsing for a struct field.

#[derive(Debug, Default, ParseAttrs, PartialEq)]
struct MyAttrs {
    /// Will parse only `#[my_attr(ident)]`.
    #[parse(ident)]
    ident: Option<syn::Ident>,

    /// Will parse `#[my_attr(value = <expr>)]`, `#[my_attr(value(<expr>))]`
    /// and `#[my_attr(value(<expr1>, <expr2>))]`.
    #[parse(value)]
    value: Vec<syn::Expr>,

    /// Will parse `#[my_attr(value <lit>)]`, `#[my_attr(value(<lit>))]`
    /// and `#[my_attr(value(<lit1>, <lit2>))]`.
    #[parse(value(spaced))]
    value_spaced: HashSet<syn::Lit>,

    /// Will parse `#[my_attr(map <ident> = <type>)]` only.
    #[parse(map)]
    map: HashMap<syn::Ident, syn::Type>,

    /// Will parse `#[my_attr(nested(<arg1>, <arg2>))]` only.
    ///
    /// Note, we use [`Box`] here only because of recursive structure.
    #[parse(nested)]
    nested: Option<Spanning<Box<MyAttrs>>>,
}

let input: syn::DeriveInput = parse_quote! {
    #[my_attr(ident)]
    #[my_attr(value = 2 * 2, value_spaced "some")]
    #[my_attr(map A = Option<u8>)]
    #[my_attr(map B = syn::Result<()>)]
    #[my_attr(nested(ident, value = "another"))]
    struct Dummy;
};
let my_attrs = MyAttrs::parse_attrs("my_attr", &input);

let expected_nested = MyAttrs {
    ident: Some(parse_quote!(ident)),
    value: vec![parse_quote!("another")],
    ..MyAttrs::default()
};

assert!(my_attrs.is_ok());
assert_eq!(my_attrs.ident, Some(parse_quote!(ident)));
assert_eq!(my_attrs.value, vec![parse_quote!(2 * 2)]);
assert!(my_attrs.value_spaced.contains(&parse_quote!("some")));
assert_eq!(my_attrs.map.len(), 2);
assert_eq!(my_attrs.map[&parse_quote!(A)], parse_quote!(Option<u8>));
assert_eq!(my_attrs.map[&parse_quote!(B)], parse_quote!(syn::Result<()>));
assert_eq!(*my_attrs.nested.unwrap().into_inner(), expected_nested);

Only one such argument can be chosen for a single field.

#[derive(Default, ParseAttrs)]
struct Wrong {
    /// We cannot use two kinds of parsing simultaneously.
    #[parse(ident, value)]
    field: Option<syn::Ident>,
}

alias = <name>, aliases(<name1>, <name2>) (optional)

Adds aliases for an attribute’s argument in addition to its field ident.

#[derive(Default, ParseAttrs)]
struct MyAttrs {
    #[parse(value, alias = value)]
    #[parse(aliases(vals, values))]
    val: Vec<syn::Lit>,
}

let input: syn::DeriveInput = parse_quote! {
    #[my_attr(val = "foo")]
    #[my_attr(value = "bar")]
    #[my_attr(vals(1, 2), values(3, 4))]
    struct Dummy;
};
let my_attrs = MyAttrs::parse_attrs("my_attr", &input);

assert_eq!(my_attrs.val.len(), 6);

arg = <name>, args(<name1>, <name2>) (optional)

Similar to alias argument, but excludes the field ident from possible names of a parsed attribute’s argument. Can be used with alias argument simultaneously.

#[derive(Default, ParseAttrs)]
struct MyAttrs {
    #[parse(value, arg = value)]
    #[parse(args(vals, values))]
    #[parse(alias = v_a_l)]
    val: Vec<syn::Lit>,
}

let input: syn::DeriveInput = parse_quote! {
    #[my_attr(value = "foo")]
    #[my_attr(vals(1, 2), values(3, 4))]
    #[my_attr(v_a_l = "bar")]
    struct Dummy;
};
let my_attrs = MyAttrs::parse_attrs("my_attr", &input);

assert_eq!(my_attrs.val.len(), 6);

let wrong: syn::DeriveInput = parse_quote! {
    #[my_attr(val = "foo")]
    struct Dummy;
};
let my_attrs = MyAttrs::parse_attrs("my_attr", &wrong);

assert!(my_attrs.is_err());

dedup = <strategy> (optional)

Defines deduplication strategy for the repeated same values during parsing. Can be one of the following:

  • unique (default): disallows duplicates;
  • first: takes first value and ignores subsequent ones;
  • last: takes last value and ignores previous ones.
#[derive(Default, ParseAttrs)]
struct MyAttrs {
    /// Picks last appeared [`syn::Ident`] in attributes.
    #[parse(ident, dedup = last, alias = named)]
    name: Option<syn::Ident>,

    /// Picks first value of `lit = <lit>` argument.
    #[parse(value, dedup = first)]
    lit: Option<syn::LitStr>,

    /// Allows only one of `args`.
    #[parse(ident, dedup = unique, args(foo, bar, baz))]
    field: Option<syn::Ident>,
}

let input: syn::DeriveInput = parse_quote! {
    #[my_attr(name, lit = "foo")]
    #[my_attr(named, lit = "bar")]
    #[my_attr(baz)]
    struct Dummy;
};
let my_attrs = MyAttrs::parse_attrs("my_attr", &input);

assert_eq!(my_attrs.name, Some(parse_quote!(named)));
assert_eq!(my_attrs.lit, Some(parse_quote!("foo")));
assert_eq!(my_attrs.field, Some(parse_quote!(baz)));

let wrong: syn::DeriveInput = parse_quote! {
    #[my_attr(foo, bar)]
    #[my_attr(baz)]
    struct Dummy;
};
let my_attrs = MyAttrs::parse_attrs("my_attr", &wrong);

assert!(my_attrs.is_err());

validate = <func> (optional)

Allows to specify a function for additional validation of the parsed field value. The signature of the function should be the following:

fn(&FieldType) -> syn::Result<()>
#[derive(Default, ParseAttrs)]
struct MyAttrs {
    #[parse(value, validate = not_foo)]
    val: Option<syn::LitStr>,
}

fn not_foo(lit: &Option<syn::LitStr>) -> syn::Result<()> {
    if lit.as_ref().map(syn::LitStr::value).as_deref() == Some("foo") {
        Err(syn::Error::new(Span::call_site(), "'foo' is not allowed"))
    } else {
        Ok(())
    }
}

let wrong: syn::DeriveInput = parse_quote! {
    #[my_attr(val = "foo")]
    struct Dummy;
};
let my_attrs = MyAttrs::parse_attrs("my_attr", &wrong);

assert!(my_attrs.is_err());

fallback = <func> (optional)

Allows to specify a function producing a fallback value for the prased field value. The signature of the function should be the following:

fn(&mut FieldType, ParsedInputType) -> syn::Result<()>

This fallback function is invoked every time the field is parsed, despite the kind of values it contains, so it’s responsibility of the fallback function to determine whether applying fallback value is actually required.

Note, that this argument accepts expressions, so you may use field::if_empty() in a combination with a parse function to receive the required signature. In such case the parse function has a way more obvious signature:

fn(ParsedInputType) -> syn::Result<ValueType>
use synthez::{field, parse, ParseAttrs};

#[derive(Default, ParseAttrs)]
struct MyAttrs {
    /// `fallback` will use doc comment as a value, if no `desc` argument is
    /// provided.
    #[parse(value, fallback = field::if_empty(parse::attr::doc))]
    desc: Option<syn::LitStr>,
}

let from_attr: syn::DeriveInput = parse_quote! {
    /// bar
    #[my_attr(desc = "foo")]
    struct Dummy;
};
let my_attrs = MyAttrs::parse_attrs("my_attr", &from_attr);

assert_eq!(my_attrs.desc, Some(parse_quote!("foo")));

let from_doc: syn::DeriveInput = parse_quote! {
    /// bar
    struct Dummy;
};
let my_attrs = MyAttrs::parse_attrs("my_attr", &from_doc);

assert_eq!(my_attrs.desc, Some(parse_quote!("bar")));