cw_orch_from_interface_derive/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{parse_macro_input, ItemEnum};
6const INTERFACE_POSTFIX: &str = "Interface";
7
8#[proc_macro_derive(FromInterface)]
9pub fn from_derive(input: TokenStream) -> TokenStream {
10    let ast = parse_macro_input!(input as ItemEnum);
11    let (impl_generics, ty_generics, where_clause) = &ast.generics.split_for_impl();
12
13    let interface_name = &ast.ident;
14    let original_name = {
15        let counter_name = interface_name.to_string();
16        // Supporting "{OriginalName}Interface" format only, to simplify macro and interface names
17        let (counter_name, _) = counter_name
18            .split_once(INTERFACE_POSTFIX)
19            .expect(r#"Interface message type supposed to have "Interface" postfix"#);
20        proc_macro2::Ident::new(counter_name, proc_macro2::Span::call_site())
21    };
22    let froms = ast.variants.into_iter().map(|variant| {
23        let variant_name = variant.ident.clone();
24        let fields = match variant.fields {
25            syn::Fields::Unnamed(variant_fields) => {
26                let variant_fields = (0..variant_fields.unnamed.len()).map(|i| {
27                    proc_macro2::Ident::new(&format!("arg{i}"), proc_macro2::Span::call_site())
28                });
29                quote!( ( #(#variant_fields,)* ) )
30            }
31            syn::Fields::Named(variant_fields) => {
32                let idents = variant_fields
33                    .named
34                    .into_iter()
35                    .map(|field| field.ident.unwrap());
36                quote!( { #(#idents,)* } )
37            }
38            syn::Fields::Unit => quote!(),
39        };
40        quote! ( #interface_name::#variant_name #fields => #original_name::#variant_name #fields )
41    });
42    quote!(
43        impl #impl_generics From<#interface_name #ty_generics> for #original_name #ty_generics
44        #where_clause
45        {
46            fn from(value: #interface_name #ty_generics) -> #original_name #ty_generics {
47                match value {
48                    #(#froms,)*
49                }
50            }
51        }
52    )
53    .into()
54}