synthez

Rust docs CI Rust 1.56+ Unsafe Forbidden

API Docs | Changelog

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] for deriving a [From] implementations.

```rust

use std::collections::HashMap;

#

use synthez::{proc_macro2::{Span, TokenStream}, quote::quote, syn};

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

pub fn derive(input: syn::DeriveInput) -> syn::Result { let attrs = Attrs::parseattrs("from", &input)?; match (attrs.forward.issome(), !attrs.custom.isempty()) { (true, true) => Err(syn::Error::newspanned( 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, #[parse(map, arg = on)] custom: HashMap, }

[derive(ToTokens)]

[totokens(append(implfrom))]

struct ForwardDefinition { ty: syn::Ident, inner_ty: syn::Type, }

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

[derive(ToTokens)]

[totokens(append(implfroms))]

struct CustomDefinitions { ty: syn::Ident, funcs: HashMap, }

impl CustomDefinitions { fn implfroms(&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::>(); sorted.sortunstableby(|(ty1, _), (ty2, _)| { quote!(#ty1).tostring().cmp(&quote!(#ty2).tostring()) }); let impls = sorted.intoiter().map(move |(fromty, func)| { quote! { impl From<#fromty> for #ty { fn from(v: #from_ty) -> Self { #func(v) } } } }); quote! { #( #impls )* } } }

fn main() {

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

let input = syn::parsequote! { #[derive(From)] #[from(on bool = Self::parsebool)] #[from(on u8 = fromu8tomaybe)] enum Maybe { Yes, No, } }; let output = quote! { impl From for Maybe { fn from(v: bool) -> Self { Self::parsebool(v) } } impl From for Maybe { fn from(v: u8) -> Self { fromu8tomaybe(v) } } }; asserteq!(derive(input).unwrap().tostring(), output.tostring());

}

```

License

This software is subject to the terms of the Blue Oak Model License 1.0.0. If a copy of the BlueOak-1.0.0 license was not distributed with this file, You can obtain one at https://blueoakcouncil.org/license/1.0.0.