ferment_macro/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::{format_ident, quote, ToTokens};
5use syn::{Data, DeriveInput, Fields, FieldsNamed, FieldsUnnamed, FnArg, ItemFn, Lit, Meta, MetaNameValue, parse_macro_input, parse_quote, Path, PatType, Signature, Variant};
6use syn::__private::TokenStream2;
7use syn::punctuated::Punctuated;
8use syn::token::Comma;
9
10
11/// The `export` procedural macro facilitates FFI (Foreign Function Interface) conversion
12/// for a given function. It handles both input arguments and output types, converting them into a format
13/// suitable for FFI boundaries.
14///
15/// # Syntax
16///
17/// The macro can be applied to any Rust function:
18///
19/// ```ignore
20/// #[ferment_macro::export]
21/// pub fn my_function(arg1: MyType1, arg2: MyType2) -> MyReturnType {
22///     // function implementation
23/// }
24/// ```
25///
26/// # Output
27///
28/// The macro will automatically generate additional FFI-compatible code around the annotated function.
29/// It converts the function into a form that can be easily invoked from C/C++ code.
30///
31/// ## Safety
32///
33/// This macro generates safety documentation specific to the function, covering the expectations
34/// and constraints of the FFI boundary.
35///
36/// ## Function Conversion
37///
38/// The macro processes the function's input arguments and return type, performing necessary transformations
39/// like memory allocation/deallocation, pointer conversion, etc., to make them FFI-compatible.
40///
41/// # Panics
42///
43/// - The macro will panic if any of the function's argument types are not supported for conversion.
44/// - The macro will also panic if the function's return type is not supported for conversion.
45///
46/// # Example
47///
48/// ```ignore
49/// #[ferment_macro::export]
50/// pub fn add(a: i32, b: i32) -> i32 {
51///     a + b
52/// }
53/// ```
54///
55/// After applying the macro, the function can be safely invoked from C/C++ code.
56///
57/// # Note
58///
59/// This macro is intended for internal use and should be used cautiously,
60/// understanding the risks associated with FFI calls.
61///
62/// # See Also
63///
64/// # Limitations
65///
66/// - The macro currently does not support Rust async functions.
67/// - Nested data structures may not be fully supported.
68///
69#[proc_macro_attribute]
70pub fn export(_attr: TokenStream, input: TokenStream) -> TokenStream {
71    input
72}
73
74
75#[proc_macro_attribute]
76pub fn register(_attr: TokenStream, input: TokenStream) -> TokenStream {
77    let input = TokenStream2::from(input);
78    let expanded = quote! {
79        #[repr(C)]
80        #input
81    };
82    TokenStream::from(expanded)
83}
84
85#[proc_macro_attribute]
86pub fn opaque(_attr: TokenStream, input: TokenStream) -> TokenStream {
87    // let input = parse_macro_input!(input as DeriveInput);
88    // let expanded = quote! {
89    //     #[repr(C)]
90    //     #input
91    // };
92    input
93    // TokenStream::from(expanded)
94}
95
96
97#[proc_macro_derive(CompositionContext)]
98pub fn composition_context_derive(input: TokenStream) -> TokenStream {
99    let input = parse_macro_input!(input as DeriveInput);
100
101    let name = &input.ident;
102    let expanded = quote!(impl crate::composable::CompositionContext for #name {});
103
104    TokenStream::from(expanded)
105}
106
107#[proc_macro_derive(MethodCall, attributes(namespace))]
108pub fn method_call_derive(input: TokenStream) -> TokenStream {
109    let input = parse_macro_input!(input as DeriveInput);
110    let name = input.ident;
111    let namespace = input.attrs.iter()
112        .find(|attr| attr.path.is_ident("namespace"))
113        .and_then(|attr| match attr.parse_meta() {
114            Ok(Meta::NameValue(MetaNameValue { lit: Lit::Str(lit), .. })) => Some(lit.parse::<Path>().expect("Invalid namespace")),
115            _ => None
116        })
117        .expect("namespace attribute is required");
118
119    let expression_enum_name = format_ident!("{}Expr", name);
120    let mut expression_variants = Punctuated::<TokenStream2, Comma>::new();
121    let mut methods = Punctuated::<TokenStream2, Comma>::new();
122    let mut exprs = Punctuated::<TokenStream2, Comma>::new();
123
124    if let Data::Enum(data) = &input.data {
125        for Variant { ident, fields, .. } in &data.variants {
126            match fields {
127                Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
128                    let field_count = unnamed.len();
129                    let field_names = (0..field_count).map(|i| format_ident!("field{}", i)).collect::<Vec<_>>();
130                    let field_types = unnamed.iter().map(|f| &f.ty).collect::<Vec<_>>();
131                    expression_variants.push(quote!(#ident(#(#field_types,)* syn::__private::TokenStream2)));
132                    methods.push(quote!(#expression_enum_name::#ident(#(#field_names,)* _) => #name::#ident(#(#field_names.clone(),)*).to_token_stream()));
133                    exprs.push(quote!(#expression_enum_name::#ident(#(#field_names,)* expr) => expr));
134                },
135                Fields::Named(FieldsNamed { named, .. }) => {
136                    let field_names = named.iter().map(|f| f.ident.clone().unwrap()).collect::<Vec<_>>();
137                    let field_types = named.iter().map(|f| &f.ty).collect::<Vec<_>>();
138                    expression_variants.push(quote!(#ident { #(#field_names: #field_types,)* expr: syn::__private::TokenStream2 }));
139                    methods.push(quote!(#expression_enum_name::#ident { #(#field_names,)* .. } => #name::#ident { #(#field_names: #field_names.clone(),)* }.to_token_stream()));
140                    exprs.push(quote!(#expression_enum_name::#ident { #(#field_names,)* expr } => expr));
141                },
142                Fields::Unit => {
143                    expression_variants.push(quote!(#ident(syn::__private::TokenStream2)));
144                    methods.push(quote!(#expression_enum_name::#ident(_) => #name::#ident.to_token_stream()));
145                    exprs.push(quote!(#expression_enum_name::#ident(expr) => expr));
146                }
147            }
148        }
149    }
150
151    let expanded = quote! {
152        #[derive(Clone, Debug)]
153        pub enum #expression_enum_name {
154            #expression_variants
155        }
156        impl crate::presentation::MethodCall for #expression_enum_name {
157            fn method(&self) -> TokenStream2 {
158                let mut tokens = TokenStream2::new();
159                let method = match self {
160                    #methods
161                };
162                let ns = syn::punctuated::Punctuated::<_, syn::token::Colon2>::from_iter([quote!(#namespace), method]);
163                tokens.append_all(vec![ns.to_token_stream()]);
164                tokens
165            }
166            fn expr(&self) -> &TokenStream2 {
167                match self {
168                    #exprs
169                }
170            }
171        }
172        impl ToTokens for #expression_enum_name {
173            fn to_tokens(&self, dst: &mut TokenStream2) {
174                (self as &dyn crate::presentation::MethodCall).to_tokens(dst)
175            }
176        }
177    };
178    TokenStream::from(expanded)
179}
180
181#[proc_macro_derive(Display)]
182pub fn to_string_derive(input: TokenStream) -> TokenStream {
183    let input = parse_macro_input!(input as DeriveInput);
184    let name = input.ident;
185    let data = match input.data {
186        Data::Enum(data) => data,
187        _ => panic!("#[derive(ToString)] is only defined for enums"),
188    };
189    let match_arms = data.variants.iter().map(|Variant { ident, fields, .. } | {
190        match fields {
191            Fields::Named(fields) => quote! { Self::#ident { .. } => format!("{}{}", stringify!(#ident), stringify!(#fields)), },
192            Fields::Unnamed(fields) => quote! { Self::#ident(..) => format!("{}{}", stringify!(#ident), stringify!(#fields)), },
193            Fields::Unit => quote! { Self::#ident => format!("{}", stringify!(#ident)), }
194        }
195        // match fields {
196        //     Fields::Named(_) => quote! { Self::#ident { .. } => stringify!(#ident).to_string(), },
197        //     Fields::Unnamed(_) => quote! { Self::#ident(..) => stringify!(#ident).to_string(), },
198        //     Fields::Unit => quote! { Self::#ident => stringify!(#ident).to_string(), }
199        // }
200    });
201
202    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
203    // impl #impl_generics crate::composer::AttrComposable<SPEC::Attr> for #ident #ty_generics #where_clause {
204
205    let expanded = quote! {
206        impl #impl_generics std::fmt::Display for #name #ty_generics #where_clause {
207            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208                f.write_str(match self {
209                    #(#match_arms)*
210                }.as_str())
211            }
212        }
213    };
214    TokenStream::from(expanded)
215}
216
217#[proc_macro_derive(BasicComposerOwner)]
218pub fn basic_composer_owner_derive(input: TokenStream) -> TokenStream {
219    let DeriveInput { ident, generics, .. } = parse_macro_input!(input as DeriveInput);
220    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
221    let expanded = quote! {
222        impl #impl_generics crate::composer::BasicComposerOwner<crate::presentable::Context, LANG, SPEC, Gen> for #ident #ty_generics #where_clause {
223            fn base(&self) -> &crate::composer::BasicComposer<crate::composer::ParentComposer<Self>, LANG> {
224                &self.base
225            }
226        }
227    };
228    TokenStream::from(expanded)
229}
230#[proc_macro_derive(ComposerBase)]
231pub fn composer_base_derive(input: TokenStream) -> TokenStream {
232    let DeriveInput { ident, generics, .. } = parse_macro_input!(input as DeriveInput);
233    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
234    let expanded = quote! {
235        impl #impl_generics crate::composer::BasicComposerOwner<LANG, SPEC> for #ident #ty_generics #where_clause {
236            fn base(&self) -> &crate::composer::BasicComposerLink<LANG, SPEC, Self> {
237                &self.base
238            }
239        }
240        impl #impl_generics crate::composer::AttrComposable<SPEC::Attr> for #ident #ty_generics #where_clause {
241            fn compose_attributes(&self) -> SPEC::Attr {
242                self.base().compose_attributes()
243            }
244        }
245        impl #impl_generics crate::composer::GenericsComposable<SPEC::Gen> for #ident #ty_generics #where_clause {
246            fn compose_generics(&self) -> SPEC::Gen {
247                self.base().compose_generics()
248            }
249        }
250        impl #impl_generics crate::composer::LifetimesComposable<SPEC::Lt> for #ident #ty_generics #where_clause {
251            fn compose_lifetimes(&self) -> SPEC::Lt {
252                self.base().compose_lifetimes()
253            }
254        }
255        impl #impl_generics crate::composer::SourceAccessible for #ident #ty_generics #where_clause {
256            fn context(&self) -> &ComposerLink<crate::context::ScopeContext> {
257                self.base().context()
258            }
259        }
260        impl #impl_generics crate::composer::TypeAspect<SPEC::TYC> for #ident #ty_generics #where_clause {
261            fn type_context_ref(&self) -> &SPEC::TYC {
262                self.base().type_context_ref()
263            }
264        }
265    };
266    TokenStream::from(expanded)
267}
268
269#[proc_macro_attribute]
270pub fn debug_io(_attr: TokenStream, item: TokenStream) -> TokenStream {
271    let input = parse_macro_input!(item as ItemFn);
272    let ItemFn { sig: Signature { ident: ref method_name, ref inputs, .. }, ref block, .. } = input;
273    let args: Vec<_> = inputs.iter().filter_map(|arg| {
274        if let FnArg::Typed(PatType { pat, .. }) = arg {
275            Some(quote! { #pat })
276        } else {
277            None
278        }
279    }).collect();
280    let fn_name = format!("{}", method_name);
281
282    let args_str = args.iter().map(ToTokens::to_token_stream).collect::<Vec<_>>();
283    let new_block = quote! {{
284        let debug_str = #fn_name;
285        let result = {
286            #block
287        };
288        println!("{}({:?}) -> {:?}", debug_str, #(#args_str),*, result);
289        result
290    }};
291    let mut output = input.clone();
292
293    output.block = parse_quote!(#new_block);
294    TokenStream::from(quote! { #output })
295}
296
297
298
299