starlane_primitive_macros/
lib.rs

1/*#![feature(proc_macro_quote)]*/
2#![crate_type = "lib"]
3#![allow(warnings)]
4#[feature("proc_macro_lib2")]
5#[macro_use]
6extern crate quote;
7
8use proc_macro::TokenStream;
9use proc_macro2::Ident;
10use proc_macro_crate::{crate_name, FoundCrate};
11use quote::quote;
12use quote::ToTokens;
13use syn::__private::TokenStream2;
14use syn::token::Mut;
15use syn::{
16    parse_file, parse_macro_input, AttrStyle, Attribute, AttributeArgs, Data, DeriveInput, Expr,
17    ExprTuple, File, FnArg, ImplItem, ItemImpl, LitStr, PatType, PathArguments, Token, Type,
18    Visibility,
19};
20
21/// Takes a given enum (which in turn accepts child enums) and auto generates a `Parent::From` so the child
22/// can turn into the parent and a `TryInto<Child> for Parent` so the Parent can attempt to turn into the child.
23/// ```
24/// #[derive(Autobox)]
25/// pub enum Parent {
26///   Child(Child)
27/// }
28///
29/// pub enum Child {
30///   Variant1,
31///   Variant2
32/// }
33/// ```
34/// Will generate something like:
35/// ```
36/// //impl Autobox for Parent { }
37///
38/// impl From<Child> for Parent {
39///   fn from( child: Child ) -> Self {
40///      Self::Child(child)
41///   }
42/// }
43///
44/// impl TryInto<Child> for Parent {
45///   type Err=ParseErrs;
46///
47///   fn try_into(self) -> Result<Child,Self::Err> {
48///     if let Self::Child(child) = self {
49///        Ok(self)
50///     } else {
51///        Err("err")
52///     }
53///   }
54/// }
55/// ```
56#[proc_macro_derive(Autobox)]
57pub fn autobox(item: TokenStream) -> TokenStream {
58    let input = parse_macro_input!(item as DeriveInput);
59    let ident = &input.ident;
60
61    let mut xforms = vec![];
62    if let Data::Enum(data) = &input.data {
63        for variant in data.variants.clone() {
64            if variant.fields.len() > 1 {
65                panic!("derive Transform only works on Enums with single value tuples")
66            }
67
68            let variant_ident = variant.ident.clone();
69
70            if variant.fields.len() == 1 {
71                let mut i = variant.fields.iter();
72                let field = i.next().unwrap().clone();
73                let ty = field.ty.clone();
74                match ty {
75                    Type::Path(path) => {
76                        let segment = path.path.segments.last().cloned().unwrap();
77                        if segment.ident == format_ident!("{}", "Box") {
78                            let ty = match segment.arguments {
79                                PathArguments::AngleBracketed(ty) => {
80                                    format_ident!("{}", ty.args.to_token_stream().to_string())
81                                }
82                                _ => panic!("expecting angle brackets"),
83                            };
84
85                            let ty_str = ty.to_string();
86
87                            xforms.push(quote! {
88                                impl TryInto<#ty> for #ident {
89                                    type Error=ParseErrs;
90
91                                    fn try_into(self) -> Result<#ty,Self::Error> {
92                                        match self {
93                                        Self::#variant_ident(val) => Ok(*val),
94                                        _ => Err(ParseErrs::new(format!("expected {}",#ty_str)))
95                                        }
96                                    }
97                                }
98
99
100                                impl From<#ty> for #ident {
101                                    fn from(f: #ty) -> #ident {
102                                    #ident::#variant_ident(Box::new(f))
103                                }
104                            }
105                                    });
106                        } else {
107                            let ty = segment.ident;
108                            let ty_str = ty.to_token_stream().to_string();
109                            xforms.push(quote! {
110                                impl TryInto<#ty> for #ident {
111                                    type Error=ParseErrs;
112
113                                    fn try_into(self) -> Result<#ty,Self::Error> {
114                                        match self {
115                                            Self::#variant_ident(val) => Ok(val),
116                                            _ => Err(ParseErrs::new(format!("expected {}",#ty_str)))
117                                        }
118                                    }
119                                }
120
121
122                                impl From<#ty> for #ident {
123                                    fn from(f: #ty) -> #ident {
124                                        #ident::#variant_ident(f)
125                                    }
126                                }
127                            });
128                        }
129                    }
130                    _ => {
131                        panic!("TransformVariants can only handle Path types")
132                    }
133                }
134            }
135        }
136    } else {
137        panic!("derive Transform only works on Enums")
138    }
139
140    let rtn = quote! { #(#xforms)* };
141
142    rtn.into()
143}
144
145#[proc_macro_derive(ToSubstance)]
146pub fn to_substance(item: TokenStream) -> TokenStream {
147    let input = parse_macro_input!(item as DeriveInput);
148    let ident = &input.ident;
149
150    let mut xforms = vec![];
151    if let Data::Enum(data) = &input.data {
152        for variant in data.variants.clone() {
153            if variant.fields.len() > 1 {
154                panic!("derive Transform only works on Enums with single value tuples")
155            }
156
157            let variant_ident = variant.ident.clone();
158
159            if variant.fields.len() == 1 {
160                let mut i = variant.fields.iter();
161                let field = i.next().unwrap().clone();
162                let ty = field.ty.clone();
163                match ty {
164                    Type::Path(path) => {
165                        let segment = path.path.segments.last().cloned().unwrap();
166                        if segment.ident == format_ident!("{}", "Box") {
167                            let ty = match segment.arguments {
168                                PathArguments::AngleBracketed(ty) => {
169                                    format_ident!("{}", ty.args.to_token_stream().to_string())
170                                }
171                                _ => panic!("expecting angle brackets"),
172                            };
173
174                            let ty_str = ty.to_string();
175
176                            xforms.push(quote! {
177                            impl ToSubstance<#ty> for #ident {
178                                fn to_substance(self) -> Result<#ty,ParseErrs> {
179                                    match self {
180                                    Self::#variant_ident(val) => Ok(*val),
181                                    _ => Err(ParseErrs::new(format!("expected {}",#ty_str)))
182                                    }
183                                }
184
185                                fn to_substance_ref(&self) -> Result<&#ty,ParseErrs> {
186                                    match self {
187                                    Self::#variant_ident(val) => Ok(val.as_ref()),
188                                    _ => Err(ParseErrs::new(format!("expected {}",#ty_str)))
189                                    }
190                                }
191                            }
192
193                                });
194                        } else {
195                            let ty = segment.ident;
196                            let ty_str = ty.to_token_stream().to_string();
197                            xforms.push(quote! {
198                            impl ToSubstance<#ty> for #ident {
199                                fn to_substance(self) -> Result<#ty,ParseErrs> {
200                                    match self {
201                                    Self::#variant_ident(val) => Ok(val),
202                                    _ => Err(ParseErrs::new(format!("expected {}",#ty_str)))
203                                    }
204                                }
205                                 fn to_substance_ref(&self) -> Result<&#ty,ParseErrs> {
206                                    match self {
207                                    Self::#variant_ident(val) => Ok(val),
208                                    _ => Err(ParseErrs::new(format!("expected {}",#ty_str)))
209                                    }
210                                }
211                            }
212
213                            });
214                        }
215                    }
216                    _ => {
217                        panic!("ToSubstance can only handle Path types")
218                    }
219                }
220            } else {
221                xforms.push(quote! {
222                impl ToSubstance<()> for #ident {
223                    fn to_substance(self) -> Result<(),ParseErrs> {
224                        match self {
225                        Self::#variant_ident => Ok(()),
226                        _ => Err(ParseErrs::new(format!("expected Empty")))
227                        }
228                    }
229                     fn to_substance_ref(&self) -> Result<&(),ParseErrs> {
230                        match self {
231                        Self::#variant_ident => Ok(&()),
232                        _ => Err(ParseErrs::new(format!("expected Empty")))
233                        }
234                    }
235                }
236
237                });
238            }
239        }
240    } else {
241        panic!("derive ToSubstance only works on Enums")
242    }
243
244    let rtn = quote! { #(#xforms)* };
245
246    rtn.into()
247}
248
249/*
250#[proc_macro_derive(MechErr)]
251pub fn mech_err(item: TokenStream) -> TokenStream {
252    let input = parse_macro_input!(item as DeriveInput);
253    let ident = &input.ident;
254
255    let from = vec![
256        quote!(Box<bincode::ErrorKind>),
257        quote!(mechtron::err::MembraneErr),
258        quote!(starlane::err::ParseErrs),
259        quote!(String),
260        quote!(&'static str),
261        quote!(mechtron::err::GuestErr),
262        quote!(std::string::FromUtf8Error),
263    ];
264
265    let rtn = quote! {
266
267        impl MechErr for #ident {
268            fn to_uni_err(self) -> starlane::err::{
269               starlane::err::SpaceErr::server_error(self.to_string())
270            }
271        }
272
273        impl From<#ident> for mechtron::err::GuestErr{
274            fn from(e: #ident) -> Self {
275                        mechtron::err::GuestErr {
276                            message: e.to_string()
277                        }
278            }
279        }
280
281        impl starlane::err::CoreReflector for #ident {
282                fn as_reflected_core(self) -> starlane::wave::core::ReflectedCore {
283                   starlane::wave::core::ReflectedCore{
284                        headers: Default::default(),
285                        status: starlane::wave::core::http2::StatusCode::from_u16(500u16).unwrap(),
286                        body: self.into().into()
287                    }
288            }
289        }
290
291
292        impl ToString for #ident {
293            fn to_string(&self) -> String {
294                self.message.clone()
295            }
296        }
297
298        #(
299            impl From<#from> for #ident {
300                fn from(e: #from ) -> Self {
301                    Self {
302                        message: e.to_string()
303                    }
304                }
305            }
306        )*
307    };
308    //println!("{}", rtn.to_string());
309    rtn)
310}
311
312 */
313
314#[proc_macro_derive(ToBase)]
315pub fn base(item: TokenStream) -> TokenStream {
316    let input = parse_macro_input!(item as DeriveInput);
317    let ident = &input.ident;
318    let base = format_ident!("{}Base", ident);
319    let mut variants: Vec<Ident> = vec![];
320
321    if let Data::Enum(data) = &input.data {
322        for variant in data.variants.clone() {
323            variants.push(variant.ident.clone());
324        }
325    }
326
327    let rtn = quote! {
328        pub enum #base {
329        #(#variants),*
330        }
331
332
333        #[allow(bindings_with_variant_name)]
334        impl ToString for #base {
335            fn to_string(&self) -> String {
336                match self {
337            #( #variants => "#variants".to_string() ),*
338                }
339            }
340        }
341    };
342
343    rtn.into()
344}
345
346#[proc_macro_derive(ToLogMark)]
347pub fn to_log_mark(item: TokenStream) -> TokenStream {
348    let input = parse_macro_input!(item as DeriveInput);
349    let ident = &input.ident;
350    let base = format_ident!("{}Base", ident);
351    let mut variants: Vec<Ident> = vec![];
352
353    if let Data::Enum(data) = &input.data {
354        for variant in data.variants.clone() {
355            variants.push(variant.ident.clone());
356        }
357    }
358
359    let rtn = quote! {
360        pub enum #base {
361        #(#variants),*
362        }
363
364
365        #[allow(bindings_with_variant_name)]
366        impl ToString for #base {
367            fn to_string(&self) -> String {
368                match self {
369            #( #variants => "#variants".to_string() ),*
370                }
371            }
372        }
373    };
374
375    rtn.into()
376}
377
378#[cfg(test)]
379mod tests {
380    #[test]
381    fn it_works() {
382        let result = 2 + 2;
383        assert_eq!(result, 4);
384    }
385}
386
387#[proc_macro_derive(EnumAsStr)]
388pub fn directed_handler(item: TokenStream) -> TokenStream {
389    TokenStream::from(quote! {})
390}
391
392#[proc_macro_attribute]
393pub fn loggerhead(_attr: TokenStream, item: TokenStream) -> TokenStream {
394    let mut out = vec![];
395    let input = parse_macro_input!(item as File);
396    for item in input.items.into_iter() {
397        let item = quote!(#item);
398        println!("running parser over {}", item);
399        out.push(item);
400    }
401
402    let rtn = quote! {
403        #(#out)*
404    };
405
406    rtn.into()
407}
408
409#[proc_macro]
410pub fn push_loc(tokens: TokenStream) -> TokenStream {
411    let crt = crt_name();
412    let tuple = parse_macro_input!(tokens as ExprTuple);
413    let mut iter = tuple.elems.into_iter();
414    let logger = iter.next().unwrap();
415    let loc = iter.next().unwrap();
416
417    let rtn = quote! {
418        {
419    let mut builder = #crt::space::log::LogMarkBuilder::default();
420    builder.package(env!("CARGO_PKG_NAME").to_string());
421    builder.file(file!().to_string());
422    builder.line(line!().to_string());
423    let mark = builder.build().unwrap();
424    #logger.push(#loc)
425            }
426        };
427
428    rtn.into()
429}
430
431#[proc_macro]
432pub fn log_span(tokens: TokenStream) -> TokenStream {
433    let crt = crt_name();
434    let input = parse_macro_input!(tokens as Expr);
435    let rtn = quote! {
436        {
437    let mut builder = #crt::space::log::LogMarkBuilder::default();
438    builder.package(env!("CARGO_PKG_NAME").to_string());
439    builder.file(file!().to_string());
440    builder.line(line!().to_string());
441    let mark = builder.build().unwrap();
442    #input.push_mark(mark)
443            }
444        };
445
446    rtn.into()
447}
448
449#[proc_macro]
450pub fn logger(item: TokenStream) -> TokenStream {
451    let crt = crt_name();
452    let log_pack = quote!(#crt::space::log);
453
454    let loc = if !item.is_empty() {
455        let expr = parse_macro_input!(item as Expr);
456        quote!( #log_pack::logger().push(#expr); )
457    } else {
458        quote!( #log_pack::logger(); )
459    };
460
461    let rtn = quote! {
462        {
463            let logger = #loc;
464    let mut builder = #log_pack::LogMarkBuilder::default();
465    builder.package(env!("CARGO_PKG_NAME").to_string());
466    builder.file(file!().to_string());
467    builder.line(line!().to_string());
468    let mark = builder.build().unwrap();
469            logger.push_mark(mark)
470            }
471        };
472
473    rtn.into()
474}
475
476#[proc_macro]
477pub fn push_mark(_item: TokenStream) -> TokenStream {
478    let crt = crt_name();
479    let logger = parse_macro_input!(_item as Expr);
480    let rtn = quote! {
481        {
482    let mut builder = #crt::space::log::LogMarkBuilder::default();
483    builder.package(env!("CARGO_PKG_NAME").to_string());
484    builder.file(file!().to_string());
485    builder.line(line!().to_string());
486    let mark  = builder.build().unwrap();
487    #logger.push_mark(mark)
488            }
489
490        };
491
492    rtn.into()
493
494}
495
496#[proc_macro]
497pub fn create_mark(_item: TokenStream) -> TokenStream {
498    let crt = crt_name();
499    let rtn = quote! {
500        {
501println!("CARGO_PKG_NAME: {}", env!("CARGO_PKG_NAME"));
502    let mut builder = #crt::space::log::LogMarkBuilder::default();
503    builder.package(env!("CARGO_PKG_NAME").to_string());
504    builder.file(file!().to_string());
505    builder.line(line!().to_string());
506    builder.build().unwrap()
507            }
508        };
509
510    rtn.into()
511}
512
513#[proc_macro]
514pub fn warn(_item: TokenStream) -> TokenStream {
515    let crt = crt_name();
516    let input = parse_macro_input!(_item as LitStr);
517    let rtn = quote! {
518
519    // pushing scope so we don't collide with
520    // any other imports or local things...
521    {
522        use starlane_primitive_macros::mark;
523        use starlane_primitive_macros::create_mark;
524        use #crt::space::log::Log;
525        use #crt::space::log::LOGGER;
526        use #crt::space::log::root_logger;
527
528        // need to push_mark somewhere around here...
529        LOGGER.try_with(|logger| {
530             logger.warn(stringify!(#input));
531        } ).map_err(|e| {
532        root_logger().warn(stringify!(#input));
533    })
534        }
535     };
536    rtn.into()
537}
538
539/*
540#[proc_macro_attribute]
541pub fn point_log(_attr: TokenStream, item: TokenStream) -> TokenStream {
542    let mut out = vec![];
543    let input = parse_macro_input!(item as File);
544    for item in input.items.into_iter() {
545        let item = quote!(#item);
546        println!("running parser over {}", item);
547        out.push(item);
548    }
549
550    let rtn = quote! {
551        #(#out)*
552    };
553
554    panic!("~~~ POINT LOG MACRO: {}",rtn.to_string());
555
556    rtn.into()
557}
558
559 */
560
561#[proc_macro_attribute]
562pub fn log(attr: TokenStream, item: TokenStream) -> TokenStream {
563    item.into()
564}
565
566#[proc_macro_attribute]
567pub fn logger_att(attr: TokenStream, item: TokenStream) -> TokenStream {
568    let surface = if attr.is_empty() {
569        format_ident!("logger")
570    } else {
571        format_ident!("{}", attr.to_string())
572    };
573
574    let item_cp = item.clone();
575    let mut impl_item = parse_macro_input!(item_cp as syn::ItemImpl);
576    //    let mut wrappers = vec![];
577    //    let mut methods = vec![];
578
579    for item_impl in &impl_item.items {
580        if let ImplItem::Method(call) = item_impl {
581            {
582                let (__async, __await) = match call.sig.asyncness {
583                    None => (quote! {}, quote! {}),
584                    Some(_) => (quote! {async}, quote! {.await}),
585                };
586
587                let mut inner_call = call.clone();
588                inner_call.vis = Visibility::Inherited;
589                inner_call.sig.ident = format_ident!("__{}", call.sig.ident);
590                inner_call.attrs = vec![];
591                /*
592                let args: Vec<TokenStream>  = inner_call.sig.inputs.clone().into_iter().map( |arg| match arg.clone() {
593
594                   FnArg::Receiver(r) => {
595                      let arg = quote!{#r};
596                       arg.to_token_stream()
597
598                   },
599                    arg => arg.to_token_stream()
600                }
601
602                ).collect_into();
603
604
605                let args = quote!{#( #args )*};
606                panic!("ARGS: {}",args.to_string());
607                 */
608                todo!();
609
610                let attributes = call.attrs.clone();
611                let vis = call.vis.clone();
612                let sig = call.sig.clone();
613                let block = call.block.clone();
614
615                call.clone();
616                let blah = quote! {
617                   #(#attributes)*
618                   #vis
619                   #__async
620                   #sig
621                    {
622                        #inner_call
623                    }
624                };
625                panic!("{}", blah);
626            }
627        }
628    }
629
630    //    TokenStream2::from_iter(vec![rtn, TokenStream2::from(item)]).into()
631    todo!()
632}
633
634fn find_impl_type(item_impl: &ItemImpl) -> Ident {
635    if let Type::Path(path) = &*item_impl.self_ty {
636        path.path.segments.last().as_ref().unwrap().ident.clone()
637    } else {
638        panic!("could not get impl name")
639    }
640}
641
642fn find_log_attr(attrs: &Vec<Attribute>) -> TokenStream {
643    for attr in attrs {
644        if attr
645            .path
646            .segments
647            .last()
648            .expect("segment")
649            .to_token_stream()
650            .to_string()
651            .as_str()
652            == "logger"
653        {
654            let rtn = quote!(#attr);
655            return rtn.into();
656        }
657    }
658    let rtn = quote!(logger);
659    rtn.into()
660}
661
662
663
664
665fn crt_name () -> TokenStream2{
666    let found_crate = crate_name("starlane").expect("my-crate is present in `Cargo.toml`");
667
668    let crt = match found_crate {
669        FoundCrate::Itself => quote!( crate ),
670        FoundCrate::Name(name) => {
671            quote!( starlane )
672        }
673    };
674    crt
675}
676