schema_bridge_macro/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident};
4
5#[proc_macro_derive(SchemaBridge, attributes(serde))]
6pub fn derive_schema_bridge(input: TokenStream) -> TokenStream {
7    let input = parse_macro_input!(input as DeriveInput);
8    let name = input.ident;
9
10    let ts_impl = impl_to_ts(&name, &input.data);
11    let schema_impl = impl_to_schema(&name, &input.data);
12
13    let expanded = quote! {
14        impl ::schema_bridge::SchemaBridge for #name {
15            fn to_ts() -> String {
16                #ts_impl
17            }
18
19            fn to_schema() -> ::schema_bridge::Schema {
20                #schema_impl
21            }
22        }
23    };
24
25    TokenStream::from(expanded)
26}
27
28fn impl_to_ts(_name: &Ident, data: &Data) -> proc_macro2::TokenStream {
29    match data {
30        Data::Struct(data) => {
31            match &data.fields {
32                Fields::Named(fields) => {
33                    let fields_ts = fields.named.iter().map(|f| {
34                        let field_name = &f.ident;
35                        let ty = &f.ty;
36                        quote! {
37                            format!("{}: {};", stringify!(#field_name), <#ty as ::schema_bridge::SchemaBridge>::to_ts())
38                        }
39                    });
40
41                    quote! {
42                        let fields = vec![#(#fields_ts),*];
43                        format!("{{ {} }}", fields.join(" "))
44                    }
45                }
46                Fields::Unnamed(fields) => {
47                    // Support for tuple structs, especially newtype pattern
48                    if fields.unnamed.len() == 1 {
49                        // Newtype pattern: delegate to the inner type
50                        let inner_ty = &fields.unnamed[0].ty;
51                        quote! {
52                            <#inner_ty as ::schema_bridge::SchemaBridge>::to_ts()
53                        }
54                    } else {
55                        // Multiple field tuple struct - represent as tuple
56                        let field_types = fields.unnamed.iter().map(|f| {
57                            let ty = &f.ty;
58                            quote! {
59                                <#ty as ::schema_bridge::SchemaBridge>::to_ts()
60                            }
61                        });
62
63                        quote! {
64                            let types = vec![#(#field_types),*];
65                            format!("[{}]", types.join(", "))
66                        }
67                    }
68                }
69                Fields::Unit => quote! { "null".to_string() },
70            }
71        }
72        Data::Enum(data) => {
73            let variants = data.variants.iter().map(|v| {
74                let variant_name = &v.ident;
75                // Simple enum for now
76                quote! {
77                    format!("'{}'", stringify!(#variant_name))
78                }
79            });
80
81            quote! {
82                let variants = vec![#(#variants),*];
83                variants.join(" | ")
84            }
85        }
86        _ => quote! { "any".to_string() },
87    }
88}
89
90fn impl_to_schema(_name: &Ident, _data: &Data) -> proc_macro2::TokenStream {
91    // Placeholder for now, focusing on TS generation first
92    quote! {
93        ::schema_bridge::Schema::Any
94    }
95}