#syn #macro-derive #codegen #derive #macro

synthez

Steroids for syn, quote and proc-macro2 crates

7个版本

0.3.1 2023年4月24日
0.3.0 2023年3月21日
0.2.0 2021年10月27日
0.1.3 2021年8月27日
0.1.0 2021年6月25日

#95 in 过程宏

Download history 16439/week @ 2024-04-01 17732/week @ 2024-04-08 17399/week @ 2024-04-15 19136/week @ 2024-04-22 25343/week @ 2024-04-29 26095/week @ 2024-05-06 24469/week @ 2024-05-13 42216/week @ 2024-05-20 36020/week @ 2024-05-27 41450/week @ 2024-06-03 42106/week @ 2024-06-10 42068/week @ 2024-06-17 43403/week @ 2024-06-24 29907/week @ 2024-07-01 24688/week @ 2024-07-08 17160/week @ 2024-07-15

每月下载量116,302
2 个crates中使用(通过 cucumber-codegen

BlueOak-1.0.0

110KB
2.5K SLoC

synthez

crates.io Rust 1.62+ Unsafe Forbidden
CI Rust docs

API文档 | 变更日志

Steroids for syn, quote and proc_macro2 crates.

Cargo功能

full

syn crate 的 full 功能相同。

启用支持表示所有有效Rust源代码语法树的复杂数据结构,包括项和表达式。

proc_macro_derive 编写示例

这是使用此库编写简化 proc_macro_derive 以生成 From 实现的示例。

# 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<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 )* }
    }
}

# fn main() {
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());
# }

许可证

此软件受 Blue Oak Model License 1.0.0 条款的约束。如果此文件未附带 BlueOak-1.0.0 许可证的副本,您可以从 https://blueoakcouncil.org/license/1.0.0 获取一个。

依赖项

~335–800KB
~19K SLoC