1 unstable release

0.1.0 Aug 25, 2019

#195 in Procedural macros

Download history 8/week @ 2021-09-25 24/week @ 2021-10-02 17/week @ 2021-10-09 30/week @ 2021-10-16 28/week @ 2021-10-23 80/week @ 2021-10-30 84/week @ 2021-11-06 94/week @ 2021-11-13 137/week @ 2021-11-20 88/week @ 2021-11-27 211/week @ 2021-12-04 22/week @ 2021-12-11 3/week @ 2021-12-18 2/week @ 2021-12-25 3/week @ 2022-01-01 262/week @ 2022-01-08

270 downloads per month

MIT license

46KB
868 lines

Build Status Documentation

serde_syn

serde_syn is a serde backend for parsing Rust syntax inside procedural macros. For example, you can deserialize parameters for a custom derive out of attributes and directly into structs. The goal is to eliminate the parsing boilerplate that goes into writing procedural macros.

The interface to serde_syn is fairly minimal. Here are a few ways to use it:

  • syn requires a Parser implementation to process syntax inside already parsed/visited attributes. The [parser] function creates exactly that!
  • If you are working directly with proc_macro/proc_macro2 token streams or strings, you should also use [parser].
  • If you are implementing syn's Parse trait yourself, you should use the [from_stream] function which takes in a ParseStream.

Lots of pre-made configurations exist inside the [config] module for common syntaxes (JSON-like, attribute-like, expression-like, etc) or you can combine flags to build your own.

Example derive implementation

Here you can see a simple derive macro implementation. For more examples, see the examples directory.

#
#
/// The format of `named` attributes.
#[derive(Deserialize)]
struct Props {
    rename: Option<String>, // #[named(rename="hello")]
    lowercase: Option<()>,  // #[named(lowercase)]
}

#[proc_macro_derive(NamedType, attributes(named))]
pub fn my_macro(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let mut name = input.ident.to_string();

    for attr in input.attrs.iter().filter(|a| a.path.is_ident("named")) {
        let parser = parser::<Props>(config::RUSTY_META);
        let props = match attr.parse_args_with(parser) {
            Ok(props) => props,
            Err(err) => return err.to_compile_error().into(),
        };

        if let Some(rename) = props.rename { name = rename; }
        if props.lowercase.is_some() { name = name.to_lowercase(); }
    }

    let ident = &input.ident;
    (quote! {
        impl NamedType for #ident {
            fn name() -> &'static str { #name }
        }
    }).into()
}

Error handling

Deserialization errors are automatically assigned a "span" (the area of the source code that could not be parsed) before being returned from [parser] and [from_stream] as ordinary syn::Errors. When that error is reported to the Rust compiler, the correct regions of code will be highlighted:

error: unknown field `lowrcase`, expected `rename` or `lowercase`
  --> named_type.rs:4:13
   |
4  |     #[named(lowrcase)]
   |             ^^^^^^^^

If you use [Deserializer] directly, serde_syn will do its best to assign a span but it is always possible to create an error with no span using serde's required custom function.

Limitations

serde_syn is early in development and so still has some gotchyas. For example, serde_syn will throw an error if you try to deserialize into a serde_json Value since it doesn't yet support self-description.

If you find any bugs, have any ideas, or wind up with free time to help random open source projects, please drop by the repository.

Dependencies

~0.4–1MB
~23K SLoC