1#![crate_type = "lib"]
2#![allow(warnings)]
3
4#[macro_use]
5extern crate lazy_static;
6#[macro_use]
7extern crate strum_macros;
8
9use proc_macro::TokenStream;
10use std::str::FromStr;
11
12use chrono::{DateTime, Utc};
13use cosmic_space::loc;
14use nom::combinator::into;
15use nom_locate::LocatedSpan;
16use proc_macro2::Ident;
17use quote::__private::ext::RepToTokensExt;
18use quote::{format_ident, quote, ToTokens, TokenStreamExt};
19use regex::Regex;
20use syn::__private::TokenStream2;
21use syn::parse::{Parse, ParseBuffer, ParseStream};
22use syn::parse_quote::ParseQuote;
23use syn::spanned::Spanned;
24use syn::token::Async;
25use syn::{
26 parse_macro_input, AngleBracketedGenericArguments, Attribute, Data, DataEnum, DataUnion,
27 DeriveInput, FieldsNamed, FieldsUnnamed, FnArg, GenericArgument, ImplItem, ItemImpl,
28 ItemStruct, PathArguments, PathSegment, ReturnType, Signature, Type, Visibility,
29};
30
31use cosmic_space::parse::route_attribute_value;
32use cosmic_space::util::log;
33use cosmic_space::wasm::Timestamp;
34
35#[no_mangle]
36extern "C" fn cosmic_uuid() -> loc::Uuid {
37 loc::Uuid::from(uuid::Uuid::new_v4().to_string()).unwrap()
38}
39
40#[no_mangle]
41extern "C" fn cosmic_timestamp() -> Timestamp {
42 Timestamp::new(Utc::now().timestamp_millis())
43}
44
45#[proc_macro_derive(DirectedHandler)]
49pub fn directed_handler(item: TokenStream) -> TokenStream {
50 TokenStream::from(quote! {})
51}
52
53#[proc_macro_attribute]
89pub fn handler(attr: TokenStream, item: TokenStream) -> TokenStream {
90 _handler(attr, item, true)
91}
92
93#[proc_macro_attribute]
94pub fn handler_sync(attr: TokenStream, item: TokenStream) -> TokenStream {
95 _handler(attr, item, false)
96}
97
98fn _handler(attr: TokenStream, item: TokenStream, _async: bool) -> TokenStream {
99 let item_cp = item.clone();
100 let mut impl_item = parse_macro_input!(item_cp as syn::ItemImpl);
101 let mut static_selectors = vec![];
103 let mut static_selector_keys = vec![];
104 let mut idents = vec![];
105 let impl_name = find_impl_type(&impl_item);
106
107 for item_impl in &impl_item.items {
110 if let ImplItem::Method(call) = item_impl {
111 if let Some(attr) = find_route_attr(&call.attrs) {
112 let internal = attr.tokens.to_token_stream().to_string();
113 idents.push(format_ident!("__{}__route", call.sig.ident.clone()));
114 let selector_ident = format_ident!("__{}_{}__", impl_name, call.sig.ident);
115 let route_selector = attr.to_token_stream().to_string();
116 static_selector_keys.push(selector_ident.clone());
117 let static_selector = quote! {
118 static ref #selector_ident : cosmic_space::config::bind::RouteSelector = cosmic_space::parse::route_attribute(#route_selector).unwrap();
119 };
120 static_selectors.push(static_selector);
121 } else {
128 }
130 } else {
131 }
133 }
134
135 let self_ty = impl_item.self_ty.clone();
138 let generics = impl_item.generics.clone();
139 let where_clause = impl_item.generics.where_clause.clone();
140
141 let attr: TokenStream2 = attr.into();
142
143 let rtn = if attr.is_empty() {
144 quote! {Ok(RespCore::not_found())}
145 } else {
146 let rtn = match _async {
147 true => quote! {
148 #attr.handle(request).await },
149 false => quote! {
150 #attr.handler.handle(request) },
151 };
152 rtn
153 };
154
155 let selector = match _async {
156 true => quote! {cosmic_space::wave::exchange::asynch::DirectedHandlerSelector},
157 false => quote! {cosmic_space::wave::exchange::synch::DirectedHandlerSelector},
158 };
159
160 let handler = match _async {
161 true => quote! {cosmic_space::wave::exchange::asynch::DirectedHandler},
162 false => quote! {cosmic_space::wave::exchange::synch::DirectedHandler},
163 };
164
165 let root_ctx = match _async {
166 true => quote! {cosmic_space::wave::exchange::asynch::RootInCtx},
167 false => quote! {cosmic_space::wave::exchange::synch::RootInCtx},
168 };
169
170 let _await = match _async {
171 true => quote! {.await},
172 false => quote! {},
173 };
174
175 let _async_trait = match _async {
176 true => quote! {#[async_trait]},
177 false => quote! {},
178 };
179
180 let _async = match _async {
181 true => quote! {async},
182 false => quote! {},
183 };
184
185 let rtn = quote! {
186 impl #generics #selector for #self_ty #where_clause{
187 fn select<'a>( &self, select: &'a cosmic_space::wave::RecipientSelector<'a>, ) -> Result<&dyn #handler, ()> {
188 if select.wave.core().method == cosmic_space::wave::core::Method::Cmd(cosmic_space::wave::core::cmd::CmdMethod::Bounce) {
189 return Ok(self);
190 }
191 #(
192 if #static_selector_keys.is_match(&select.wave).is_ok() {
193 return Ok(self);
194 }
195 )*
196 Err(())
197 }
198 }
199
200 #_async_trait
201 impl #generics #handler for #self_ty #where_clause{
202 #_async fn handle( &self, ctx: #root_ctx) -> cosmic_space::wave::core::CoreBounce {
203 #(
204 if #static_selector_keys.is_match(&ctx.wave).is_ok() {
205 return self.#idents( ctx )#_await;
206 }
207 )*
208 if ctx.wave.core().method == cosmic_space::wave::core::Method::Cmd(cosmic_space::wave::core::cmd::CmdMethod::Bounce) {
209 return self.bounce(ctx)#_await;
210 }
211 ctx.not_found().into()
212 }
213 }
214
215 lazy_static! {
216 #( #static_selectors )*
217 }
218
219 };
220
221 TokenStream2::from_iter(vec![rtn, TokenStream2::from(item)]).into()
224}
225
226fn find_impl_type(item_impl: &ItemImpl) -> Ident {
227 if let Type::Path(path) = &*item_impl.self_ty {
228 path.path.segments.last().as_ref().unwrap().ident.clone()
229 } else {
230 panic!("could not get impl name")
231 }
232}
233
234fn find_route_attr(attrs: &Vec<Attribute>) -> Option<Attribute> {
235 for attr in attrs {
236 if attr
237 .path
238 .segments
239 .last()
240 .expect("segment")
241 .to_token_stream()
242 .to_string()
243 .as_str()
244 == "route"
245 {
246 return Some(attr.clone());
247 }
248 }
249 return None;
250}
251
252#[proc_macro_attribute]
261pub fn route(attr: TokenStream, input: TokenStream) -> TokenStream {
262 let input = parse_macro_input!(input as syn::ImplItemMethod);
265
266 log(route_attribute_value(attr.to_string().as_str())).expect("valid route selector");
267
268 let params: Vec<FnArg> = input.sig.inputs.clone().into_iter().collect();
273 let ctx = params
274 .get(1)
275 .expect("route expected InCtx<I,M> as first parameter");
276 let ctx = messsage_ctx(ctx).expect("route expected InCtx<I,M> as first parameter");
277
278 let __await = match input.sig.asyncness {
279 None => quote! {},
280 Some(_) => quote! {.await},
281 };
282
283 let root_ctx = match input.sig.asyncness {
284 None => quote! {cosmic_space::wave::exchange::synch::RootInCtx},
285 Some(_) => quote! {cosmic_space::wave::exchange::asynch::RootInCtx},
286 };
287
288 let in_ctx = match input.sig.asyncness {
289 None => quote! {cosmic_space::wave::exchange::synch::InCtx},
290 Some(_) => quote! {cosmic_space::wave::exchange::asynch::InCtx},
291 };
292
293 let __async = match input.sig.asyncness {
294 None => quote! {},
295 Some(_) => quote! {async},
296 };
297
298 let orig = input.sig.ident.clone();
299 let ident = format_ident!("__{}__route", input.sig.ident);
300 let rtn_type = rtn_type(&input.sig.output);
301 let item = ctx.item;
302
303 let expanded = quote! {
304 #__async fn #ident( &self, mut ctx: #root_ctx ) -> cosmic_space::wave::core::CoreBounce {
305 let ctx: #in_ctx<'_,#item> = match ctx.push::<#item>() {
306 Ok(ctx) => ctx,
307 Err(err) => {
308 if ctx.wave.is_signal() {
309 return cosmic_space::wave::core::CoreBounce::Absorbed;
310 }
311 else {
312 return cosmic_space::wave::core::CoreBounce::Reflected(err.into());
313 }
314 }
315 };
316
317 let result = self.#orig(ctx)#__await;
318 #rtn_type
319 }
320
321 #input
322
323 };
324
325 TokenStream::from(expanded)
327}
328
329pub(crate) enum Item {
330 Request,
331 RequestCore,
332 Payload,
333}
334
335impl FromStr for Item {
336 type Err = String;
337
338 fn from_str(s: &str) -> Result<Self, Self::Err> {
339 match s {
340 "Request" => Ok(Item::Request),
341 "RequestCore" => Ok(Item::RequestCore),
342 "Payload" => Ok(Item::Payload),
343 what => panic!("cannot convert Request to type '{}'", what),
344 }
345 }
346}
347
348pub(crate) struct RequestCtx {
349 pub item: GenericArgument,
350}
351
352fn messsage_ctx(input: &FnArg) -> Result<RequestCtx, String> {
353 if let FnArg::Typed(i) = input {
354 if let Type::Path(path) = &*i.ty {
355 if let PathArguments::AngleBracketed(generics) = &path
356 .path
357 .segments
358 .last()
359 .expect("expected last segment")
360 .arguments
361 {
362 let mut args = generics.args.clone();
363 let item = args
364 .pop()
365 .expect("expecting a generic for Context Item")
366 .into_value();
367
368 let ctx = RequestCtx { item };
369
370 return Ok(ctx);
371 }
372 }
373 }
374 Err("Parameter is not a RequestCtx".to_string())
375}
376
377fn rtn_type(output: &ReturnType) -> TokenStream2 {
378 match output {
379 ReturnType::Default => {
380 quote! {cosmic_space::wave::Bounce::Absorbed}
381 }
382 ReturnType::Type(_, path) => {
383 if let Type::Path(path) = &**path {
384 let PathSegment { ident, arguments } = path.path.segments.last().unwrap();
385 match ident.to_string().as_str() {
386 "Result" => {
387 if let PathArguments::AngleBracketed(brackets) = arguments {
388 let arg = brackets.args.first().unwrap();
389 if "Substance" == arg.to_token_stream().to_string().as_str() {
390 quote! {
391 use cosmic_space::err::CoreReflector;
392 match result {
393 Ok(rtn) => cosmic_space::wave::core::CoreBounce::Reflected(cosmic_space::wave::core::ReflectedCore::ok_body(rtn)),
394 Err(err) => cosmic_space::wave::core::CoreBounce::Reflected(err.as_reflected_core())
395 }
396 }
397 } else {
398 quote! {
399 use cosmic_space::err::CoreReflector;
400 match result {
401 Ok(rtn) => cosmic_space::wave::core::CoreBounce::Reflected(rtn.into()),
402 Err(err) => cosmic_space::wave::core::CoreBounce::Reflected(err.as_reflected_core())
403 }
404 }
405 }
406 } else {
407 panic!("Result without angle brackets")
408 }
409 }
410 "Bounce" => {
411 quote! {
412 let rtn : cosmic_space::wave::core::CoreBounce = result.to_core_bounce();
413 rtn
414 }
415 }
416 "CoreBounce" => {
417 quote! {
418 result
419 }
420 }
421 "ReflectedCore" => {
422 quote! {
423 cosmic_space::wave::core::CoreBounce::Reflected(result)
424 }
425 }
426 what => {
427 panic!("unknown return type: {}", what);
428 }
429 }
430 } else {
431 panic!("expecting a path segment")
432 }
433 }
434 }
435}
436
437struct RouteAttr {
438 attribute: Attribute,
439}
440
441impl Parse for RouteAttr {
442 fn parse(input: ParseStream) -> syn::Result<Self> {
443 let mut attribute = input.call(Attribute::parse_outer)?;
444 Ok(RouteAttr {
445 attribute: attribute.remove(0),
446 })
447 }
448}