luminos_container_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{
4    parse_macro_input, FnArg, ImplItem, ItemImpl, Pat, PatType, PathArguments, Type, Error,
5};
6
7#[allow(dead_code)]
8#[proc_macro_attribute]
9pub fn injectable(_attr: TokenStream, item: TokenStream) -> TokenStream {
10    // Parse the impl block
11    let input = parse_macro_input!(item as ItemImpl);
12
13    // Find fn new(...)
14    let new_fn = input.items.iter().find_map(|it| {
15        if let ImplItem::Fn(f) = it {
16            if f.sig.ident == "new" {
17                Some(f)
18            } else {
19                None
20            }
21        } else {
22            None
23        }
24    });
25
26    let new_fn = match new_fn {
27        Some(f) => f,
28        None => {
29            return Error::new_spanned(
30                input.self_ty,
31                "expected an `impl` block containing `fn new(...) -> Self`",
32            )
33            .to_compile_error()
34            .into();
35        }
36    };
37
38    // Struct to hold parameter info
39    struct Param {
40        ident: syn::Ident,
41        ty: Type,           // Full type as declared: Arc<MyRepository>
42        inner_ty: Type,     // Type to resolve: MyRepository
43        is_arc: bool,
44    }
45
46    let mut params = Vec::new();
47
48    // Extract parameters
49    for arg in new_fn.sig.inputs.iter() {
50        if let FnArg::Typed(PatType { pat, ty, .. }) = arg {
51            if let Pat::Ident(pat_ident) = &**pat {
52                let ident = pat_ident.ident.clone();
53                let full_ty = (**ty).clone();
54                let mut is_arc = false;
55                let mut inner_ty = full_ty.clone();
56
57                // Detect Arc<T> and extract T
58                if let Type::Path(type_path) = &**ty {
59                    if let Some(seg) = type_path.path.segments.last() {
60                        if seg.ident == "Arc" {
61                            is_arc = true;
62
63                            // Extract the inner type T from Arc<T>
64                            if let PathArguments::AngleBracketed(args) = &seg.arguments {
65                                if let Some(syn::GenericArgument::Type(inner)) = args.args.first() {
66                                    inner_ty = inner.clone();
67                                }
68                            }
69                        }
70                    }
71                }
72
73                params.push(Param {
74                    ident,
75                    ty: full_ty,
76                    inner_ty,
77                    is_arc,
78                });
79            } else {
80                return Error::new_spanned(
81                    pat,
82                    "unsupported parameter pattern; expected identifier patterns like `x: Type`",
83                )
84                .to_compile_error()
85                .into();
86            }
87        }
88    }
89
90    // Field declarations (use full type)
91    let field_decls = params.iter().map(|p| {
92        let ident = &p.ident;
93        let ty = &p.ty;
94        quote! { #ident: #ty }
95    });
96
97    // Resolve calls (resolve inner type, which returns Arc<T>)
98    let resolve_calls = params.iter().map(|p| {
99        let inner_ty = &p.inner_ty;
100        quote! { c.resolve::<#inner_ty>() }
101    });
102
103    // Struct name
104    let struct_ident = match &*input.self_ty {
105        syn::Type::Path(tp) => &tp.path.segments.last().unwrap().ident,
106        _ => {
107            return Error::new_spanned(
108                input.self_ty,
109                "unsupported self type in impl; expected a simple type path",
110            )
111            .to_compile_error()
112            .into();
113        }
114    };
115
116    // Generate tokens
117    let expanded = quote! {
118        // Generated struct
119        struct #struct_ident {
120            #(#field_decls),*
121        }
122
123        // Original impl block
124        #input
125
126        // Implement Injectable trait
127        impl ::luminos_contracts::container::Injectable for #struct_ident {
128            fn __register<C: ::luminos_contracts::container::Contract>(container: &C) {
129                container.bind::<#struct_ident, _>(|c| {
130                    ::std::sync::Arc::new(Self::new(#(#resolve_calls),*))
131                });
132            }
133        }
134    };
135
136    TokenStream::from(expanded)
137}