luminos_container_macros/
lib.rs1use 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 let input = parse_macro_input!(item as ItemImpl);
12
13 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 Param {
40 ident: syn::Ident,
41 ty: Type, inner_ty: Type, is_arc: bool,
44 }
45
46 let mut params = Vec::new();
47
48 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 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 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 let field_decls = params.iter().map(|p| {
92 let ident = &p.ident;
93 let ty = &p.ty;
94 quote! { #ident: #ty }
95 });
96
97 let resolve_calls = params.iter().map(|p| {
99 let inner_ty = &p.inner_ty;
100 quote! { c.resolve::<#inner_ty>() }
101 });
102
103 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 let expanded = quote! {
118 struct #struct_ident {
120 #(#field_decls),*
121 }
122
123 #input
125
126 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}