1mod handler;
2
3use handler::find_handler_methods;
4use proc_macro::TokenStream;
5use quote::quote;
6use syn::{DeriveInput, ItemImpl, parse_macro_input};
7
8#[proc_macro_derive(Bean)]
11pub fn derive_bean(input: TokenStream) -> TokenStream {
12 let input = parse_macro_input!(input as DeriveInput);
13
14 let name = &input.ident;
16
17 let expanded = quote! {
18 impl camel_bean::BeanProcessor for #name {
20 async fn call(
21 &self,
22 method: &str,
23 exchange: &mut camel_api::Exchange,
24 ) -> Result<(), camel_api::CamelError> {
25 unimplemented!("Bean derive macro requires impl block with #[handler] methods")
26 }
27
28 fn methods(&self) -> Vec<&'static str> {
29 vec![]
30 }
31 }
32 };
33
34 TokenStream::from(expanded)
35}
36
37#[proc_macro_attribute]
41pub fn handler(_attr: TokenStream, item: TokenStream) -> TokenStream {
42 item
45}
46
47#[proc_macro_attribute]
67pub fn bean_impl(_attr: TokenStream, item: TokenStream) -> TokenStream {
68 let input = parse_macro_input!(item as ItemImpl);
69
70 match bean_impl_gen(input) {
71 Ok(tokens) => tokens,
72 Err(err) => err.to_compile_error().into(),
73 }
74}
75
76fn bean_impl_gen(item: ItemImpl) -> Result<TokenStream, syn::Error> {
78 let self_ty = &item.self_ty;
79
80 let handlers = find_handler_methods(&item.items)?;
82
83 if handlers.is_empty() {
84 return Err(syn::Error::new_spanned(
85 self_ty,
86 "No #[handler] methods found in impl block",
87 ));
88 }
89
90 let match_arms: Vec<_> = handlers
92 .iter()
93 .map(|handler| {
94 let method_name = &handler.name;
95
96 let (param_extraction, method_call) = generate_handler_invocation(handler)?;
98
99 let result_handling = generate_result_handling(handler)?;
101
102 Ok(quote! {
103 #method_name => {
104 #param_extraction
105 let result = #method_call;
106 #result_handling
107 }
108 })
109 })
110 .collect::<Result<Vec<_>, syn::Error>>()?;
111
112 let method_names: Vec<_> = handlers.iter().map(|h| h.name.as_str()).collect();
114
115 let expanded = quote! {
116 #item
117
118 #[::camel_bean::async_trait]
119 impl ::camel_bean::BeanProcessor for #self_ty {
120 async fn call(
121 &self,
122 method: &str,
123 exchange: &mut ::camel_api::Exchange,
124 ) -> Result<(), ::camel_api::CamelError> {
125 match method {
126 #(#match_arms)*
127 _ => Err(::camel_api::CamelError::ProcessorError(
128 format!("Method '{}' not found", method)
129 ))
130 }
131 }
132
133 fn methods(&self) -> Vec<&'static str> {
134 vec![#(#method_names),*]
135 }
136 }
137 };
138
139 Ok(TokenStream::from(expanded))
140}
141
142fn generate_handler_invocation(
144 handler: &handler::HandlerMethod,
145) -> Result<(proc_macro2::TokenStream, proc_macro2::TokenStream), syn::Error> {
146 let method_ident = &handler.ident;
147
148 let mut params = Vec::new();
150 let mut extraction = Vec::new();
151
152 if let Some(body_type) = &handler.body_type {
154 extraction.push(quote! {
155 let body_json = match &exchange.input.body {
156 ::camel_api::Body::Json(value) => value.clone(),
157 _ => return Err(::camel_api::CamelError::TypeConversionFailed(
158 "Expected JSON body".to_string()
159 )),
160 };
161 let body: #body_type = serde_json::from_value(body_json)
162 .map_err(|e| ::camel_api::CamelError::TypeConversionFailed(
163 format!("Failed to deserialize body: {}", e)
164 ))?;
165 });
166 params.push(quote! { body });
167 }
168
169 if handler.has_headers {
171 extraction.push(quote! {
172 let headers = exchange.input.headers.clone();
173 });
174 params.push(quote! { headers });
175 }
176
177 if handler.has_exchange {
179 params.push(quote! { exchange });
180 }
181
182 let extraction_code = quote! { #(#extraction)* };
183 let method_call = quote! { self.#method_ident(#(#params),*).await };
184
185 Ok((extraction_code, method_call))
186}
187
188fn generate_result_handling(
190 handler: &handler::HandlerMethod,
191) -> Result<proc_macro2::TokenStream, syn::Error> {
192 if handler.return_type.is_none() {
194 return Ok(quote! {
195 result.map_err(|e| ::camel_api::CamelError::ProcessorError(e.to_string()))?;
196 Ok(())
197 });
198 }
199
200 if handler.has_exchange && handler.body_type.is_none() {
202 return Ok(quote! {
204 result.map_err(|e| ::camel_api::CamelError::ProcessorError(e.to_string()))?;
205 Ok(())
206 });
207 }
208
209 Ok(quote! {
212 let value = result.map_err(|e| ::camel_api::CamelError::ProcessorError(e.to_string()))?;
213 exchange.input.body = ::camel_api::Body::Json(serde_json::to_value(value)
214 .map_err(|e| ::camel_api::CamelError::TypeConversionFailed(
215 format!("Failed to serialize result: {}", e)
216 ))?);
217 Ok(())
218 })
219}