Crate synthez[][src]

Expand description

Steroids for syn, quote and proc_macro2 crates.

Cargo features

full

Same as full feature of syn crate.

Enables support of data structures for representing the syntax tree of all valid Rust source code, including items and expressions.

Example of writing proc_macro_derive

This is an example of how this library can be used to write a simplified proc_macro_derive implemention for deriving From implementations.

use synthez::{DataExt as _, ParseAttrs, ToTokens};

pub fn derive(input: syn::DeriveInput) -> syn::Result<TokenStream> {
    let attrs = Attrs::parse_attrs("from", &input)?;
    match (attrs.forward.is_some(), !attrs.custom.is_empty()) {
        (true, true) => Err(syn::Error::new_spanned(
            input,
            "`forward` and `on` arguments are mutually exclusive",
        )),
        (false, false) => Err(syn::Error::new_spanned(
            input,
            "either `forward` or `on` argument is expected",
        )),

        // #[from(forward)]
        (true, _) => {
            if !matches!(&input.data, syn::Data::Struct(_)) {
                return Err(syn::Error::new_spanned(
                    input,
                    "only tuple structs can forward-derive From",
                ));
            }
            let fields = input.data.unnamed_fields()?;
            if fields.len() > 1 {
                return Err(syn::Error::new_spanned(
                    fields,
                    "only single-field tuple structs can forward-derive \
                     From",
                ));
            }
            let definition = ForwardDefinition {
                ty: input.ident,
                inner_ty: fields.into_iter().last().unwrap().ty,
            };
            Ok(quote! {
                #definition
            })
        }

        // #[from(on <type> = <func>)]
        (_, true) => {
            let definitions =
                CustomDefinitions { ty: input.ident, funcs: attrs.custom };
            Ok(quote! {
                #definitions
            })
        }
    }
}

#[derive(Default, ParseAttrs)]
struct Attrs {
    #[parse(ident)]
    forward: Option<syn::Ident>,
    #[parse(map, arg = on)]
    custom: HashMap<syn::Type, syn::Expr>,
}

#[derive(ToTokens)]
#[to_tokens(append(impl_from))]
struct ForwardDefinition {
    ty: syn::Ident,
    inner_ty: syn::Type,
}

impl ForwardDefinition {
    fn impl_from(&self) -> TokenStream {
        let (ty, inner_ty) = (&self.ty, &self.inner_ty);
        quote! {
            impl<T> From<T> for #ty where #inner_ty: From<T> {
                fn from(v: T) -> Self {
                    Self(v.into())
                }
            }
        }
    }
}

#[derive(ToTokens)]
#[to_tokens(append(impl_froms))]
struct CustomDefinitions {
    ty: syn::Ident,
    funcs: HashMap<syn::Type, syn::Expr>,
}

impl CustomDefinitions {
    fn impl_froms(&self) -> TokenStream {
        let ty = &self.ty;
        // We sort here for tests below not failing due to undetermined
        // order only. Real implementation may omit this.
        let mut sorted = self.funcs.iter().collect::<Vec<_>>();
        sorted.sort_unstable_by(|(ty1, _), (ty2, _)| {
            quote!(#ty1).to_string().cmp(&quote!(#ty2).to_string())
        });
        let impls = sorted.into_iter().map(move |(from_ty, func)| {
            quote! {
                impl From<#from_ty> for #ty {
                    fn from(v: #from_ty) -> Self {
                        #func(v)
                    }
                }
            }
        });
        quote! { #( #impls )* }
    }
}

let input = syn::parse_quote! {
    #[derive(From)]
    #[from(forward)]
    struct Id(u64);
};
let output = quote! {
    impl<T> From<T> for Id where u64: From<T> {
        fn from(v: T) -> Self {
            Self(v.into())
        }
    }
};
assert_eq!(derive(input).unwrap().to_string(), output.to_string());

let input = syn::parse_quote! {
    #[derive(From)]
    #[from(on bool = Self::parse_bool)]
    #[from(on u8 = from_u8_to_maybe)]
    enum Maybe {
        Yes,
        No,
    }
};
let output = quote! {
    impl From<bool> for Maybe {
        fn from(v: bool) -> Self {
            Self::parse_bool(v)
        }
    }
    impl From<u8> for Maybe {
        fn from(v: u8) -> Self {
            from_u8_to_maybe(v)
        }
    }
};
assert_eq!(derive(input).unwrap().to_string(), output.to_string());

Re-exports

pub use synthez_core::proc_macro2;
pub use synthez_core::quote;
pub use synthez_core::syn;

Modules

Extensions for [syn] types.

Batteries for working with struct fields.

Abstraction over [syn] types containing something.

Batteries for syn::parse.

Batteries for Span and [syn::spanned].

Structs

Container requiring a field to have a value mandatory.

Wrapper for non-Spanned types to hold their Span.

Traits

Extension of a [syn::Data].

Extension of a syn::Ident.

Parseing of [syn::Attribute]s into a custom defined struct.

Extension of a [syn::parse::ParseBuffer] providing common function widely used by this crate for parsing.

Types that can be interpolated inside a quote! invocation.

Derive Macros

Deriving of synthez::ParseAttrs along with a [syn::parse::Parse] implementation to parse [syn::Attribute]s into a custom defined struct.

Deriving of a quote::ToTokens implementation.