1mod handler;
2
3use handler::find_handler_methods;
4use proc_macro::TokenStream;
5use quote::quote;
6use syn::{ItemImpl, parse_macro_input};
7
8#[proc_macro_derive(Bean)]
9pub fn derive_bean(_input: TokenStream) -> TokenStream {
10 quote! {
11 compile_error!("Bean derive macro is not yet implemented. Use #[bean_impl] on an impl block instead.");
12 }
13 .into()
14}
15
16#[proc_macro_attribute]
20pub fn handler(_attr: TokenStream, item: TokenStream) -> TokenStream {
21 item
24}
25
26#[proc_macro_attribute]
46pub fn bean_impl(_attr: TokenStream, item: TokenStream) -> TokenStream {
47 let input = parse_macro_input!(item as ItemImpl);
48
49 match bean_impl_gen(input) {
50 Ok(tokens) => tokens,
51 Err(err) => err.to_compile_error().into(),
52 }
53}
54
55fn bean_impl_gen(item: ItemImpl) -> Result<TokenStream, syn::Error> {
57 let self_ty = &item.self_ty;
58
59 let handlers = find_handler_methods(&item.items)?;
61
62 if handlers.is_empty() {
63 return Err(syn::Error::new_spanned(
64 self_ty,
65 "No #[handler] methods found in impl block",
66 ));
67 }
68
69 let match_arms: Vec<_> = handlers
71 .iter()
72 .map(|handler| {
73 let method_name = &handler.name;
74
75 let (param_extraction, method_call) = generate_handler_invocation(handler)?;
77
78 let result_handling = generate_result_handling(handler)?;
80
81 Ok(quote! {
82 #method_name => {
83 #param_extraction
84 let result = #method_call;
85 #result_handling
86 }
87 })
88 })
89 .collect::<Result<Vec<_>, syn::Error>>()?;
90
91 let method_names: Vec<_> = handlers.iter().map(|h| h.name.as_str()).collect();
93
94 let expanded = quote! {
95 #item
96
97 #[::camel_bean::async_trait]
98 impl ::camel_bean::BeanProcessor for #self_ty {
99 async fn call(
100 &self,
101 method: &str,
102 exchange: &mut ::camel_api::Exchange,
103 ) -> Result<(), ::camel_api::CamelError> {
104 match method {
105 #(#match_arms)*
106 _ => Err(::camel_api::CamelError::ProcessorError(
107 format!("Method '{}' not found", method)
108 ))
109 }
110 }
111
112 fn methods(&self) -> Vec<String> {
113 vec![#(#method_names.to_string()),*]
114 }
115 }
116 };
117
118 Ok(TokenStream::from(expanded))
119}
120
121fn generate_handler_invocation(
123 handler: &handler::HandlerMethod,
124) -> Result<(proc_macro2::TokenStream, proc_macro2::TokenStream), syn::Error> {
125 let method_ident = &handler.ident;
126
127 let mut params = Vec::new();
129 let mut extraction = Vec::new();
130
131 if let Some(body_type) = &handler.body_type {
133 extraction.push(quote! {
134 let body_json = match &exchange.input.body {
135 ::camel_api::Body::Json(value) => value.clone(),
136 _ => return Err(::camel_api::CamelError::TypeConversionFailed(
137 "Expected JSON body".to_string()
138 )),
139 };
140 let body: #body_type = serde_json::from_value(body_json)
141 .map_err(|e| ::camel_api::CamelError::TypeConversionFailed(
142 format!("Failed to deserialize body: {}", e)
143 ))?;
144 });
145 params.push(quote! { body });
146 }
147
148 if handler.has_headers {
150 extraction.push(quote! {
151 let headers = exchange.input.headers.clone();
152 });
153 params.push(quote! { headers });
154 }
155
156 if handler.has_exchange {
158 params.push(quote! { exchange });
159 }
160
161 let extraction_code = quote! { #(#extraction)* };
162 let method_call = quote! { self.#method_ident(#(#params),*).await };
163
164 Ok((extraction_code, method_call))
165}
166
167fn generate_result_handling(
169 handler: &handler::HandlerMethod,
170) -> Result<proc_macro2::TokenStream, syn::Error> {
171 if handler.return_type.is_none() {
173 return Ok(quote! {
174 result.map_err(|e| ::camel_api::CamelError::ProcessorError(e.to_string()))?;
175 Ok(())
176 });
177 }
178
179 if handler.has_exchange && handler.body_type.is_none() {
181 return Ok(quote! {
183 result.map_err(|e| ::camel_api::CamelError::ProcessorError(e.to_string()))?;
184 Ok(())
185 });
186 }
187
188 Ok(quote! {
191 let value = result.map_err(|e| ::camel_api::CamelError::ProcessorError(e.to_string()))?;
192 exchange.input.body = ::camel_api::Body::Json(serde_json::to_value(value)
193 .map_err(|e| ::camel_api::CamelError::TypeConversionFailed(
194 format!("Failed to serialize result: {}", e)
195 ))?);
196 Ok(())
197 })
198}