#![feature(str_as_str)] #![feature(box_patterns)] extern crate proc_macro; pub(crate) use std::collections::BTreeMap; pub(crate) use std::cmp::Ordering; pub(crate) use std::sync::Arc; pub(crate) use proc_macro::TokenStream; pub(crate) use proc_macro2::{ TokenStream as TokenStream2, Ident, Span, Punct, Group, Delimiter, Spacing::* }; pub(crate) use syn::{ parse_macro_input, ImplItem, ImplItemFn, LitStr, Type, TypePath, ItemImpl, ReturnType, Signature, FnArg, Pat, PatType, PatIdent, parse::{Parse, ParseStream, Result}, }; pub(crate) use quote::{quote, TokenStreamExt, ToTokens}; pub(crate) use heck::{AsKebabCase, AsUpperCamelCase}; mod proc_view; mod proc_expose; mod proc_command; #[cfg(test)] use syn::parse_quote as pq; #[proc_macro_attribute] pub fn expose (meta: TokenStream, item: TokenStream) -> TokenStream { use self::proc_expose::{ExposeDef, ExposeMeta, ExposeImpl}; write(ExposeDef( parse_macro_input!(meta as ExposeMeta), parse_macro_input!(item as ExposeImpl), )) } #[proc_macro_attribute] pub fn command (meta: TokenStream, item: TokenStream) -> TokenStream { use self::proc_command::{CommandDef, CommandMeta, CommandImpl}; write(CommandDef( parse_macro_input!(meta as CommandMeta), parse_macro_input!(item as CommandImpl), )) } #[proc_macro_attribute] pub fn view (meta: TokenStream, item: TokenStream) -> TokenStream { use self::proc_view::{ViewDef, ViewMeta, ViewImpl}; write(ViewDef( parse_macro_input!(meta as ViewMeta), parse_macro_input!(item as ViewImpl), )) } pub(crate) fn write (t: T) -> TokenStream { let mut out = TokenStream2::new(); t.to_tokens(&mut out); out.into() } pub(crate) fn write_quote (quote: TokenStream2) -> TokenStream2 { let mut out = TokenStream2::new(); for token in quote { out.append(token); } out } pub(crate) fn write_quote_to (out: &mut TokenStream2, quote: TokenStream2) { for token in quote { out.append(token); } } #[cfg(test)] #[test] fn test_proc_view () { let x: crate::proc_view::ViewMeta = pq! { SomeOutput }; let output: Ident = pq! { SomeOutput }; assert_eq!(x.output, output); // TODO let _x: crate::proc_view::ViewImpl = pq! { impl Foo { /// docstring1 #[tengri::view(":view1")] #[bar] fn a_view () {} #[baz] /// docstring2 #[baz] fn is_not_view () {} } }; let _expected_target: Ident = pq! { Foo }; //assert_eq!(x.target, expected_target); //assert_eq!(x.items.len(), 2); //assert_eq!(x.items[0].item, pq! { ///// docstring1 //#[bar] fn a_view () {} //}); //assert_eq!(x.items[1].item, pq! { //#[baz] ///// docstring2 //#[baz] fn is_not_view () {} //}); //assert_eq!(x.syms, vec![ //ViewArm( { symbol: pq! { ":view1" }, name: pq! { a_view }, }, //]); // FIXME //let parsed: ViewDefinition = pq! { //#[tengri_proc::view(SomeOutput)] //impl SomeView { //#[tengri::view(":view-1")] //fn view_1 (&self) -> impl Render + use<'_> { //"view-1" //} //} //}; //let written = quote! { #parsed }; //assert_eq!(format!("{written}"), format!("{}", quote! { //impl SomeView { //fn view_1 (&self) -> impl Render + use<'_> { //"view-1" //} //} ///// Generated by [tengri_proc]. //impl ::tengri::output::Content for SomeView { //fn content (&self) -> impl Render { //self.size.of(::tengri::output::View(self, self.config.view)) //} //} ///// Generated by [tengri_proc]. //impl<'a> ::tengri::dsl::ViewContext<'a, SomeOutput> for SomeView { //fn get_content_sym (&'a self, value: &Value<'a>) -> Option> { //match value { //::tengri::dsl::Value::Sym(":view-1") => self.view_1().boxed(), //_ => panic!("expected Sym(content), got: {value:?}") //} //} //} //})); } //#[cfg(test)] #[test] fn test_expose_definition () { // TODO //let parsed: ExposeImpl = pq! { ////#[tengri_proc::expose] //impl Something { //fn something () -> bool {} //} //}; //// FIXME: ////assert_eq!( ////format!("{}", quote! { #parsed }), ////format!("{}", quote! { ////impl Something { ////fn something () {} ////} ////impl ::tengri::Context for Something { ////fn get (&self, dsl: &::tengri::Value) -> Option { ////Some(match dsl { ////::tengri::Value::Sym(":true") => true, ////::tengri::Value::Sym(":false") => false, ////::tengri::Value::Sym(":bool1") => true || false, ////_ => return None ////}) ////} ////} ////}) ////); //let parsed: ExposeImpl = pq! { ////#[tengri_proc::expose] //impl Something { //#[tengri::expose(bool)] { //":bool1" => true || false, //} //#[tengri::expose(u16)] { //":u161" => 0 + 1, //} //#[tengri::expose(usize)] { //":usize1" => 1 + 2, //} //#[tengri::expose(Arc)] { //":arcstr1" => "foo".into(), //} //#[tengri::expose(Option>)] { //":optarcstr1" => Some("bar".into()), //":optarcstr2" => Some("baz".into()), //} //fn something () {} //} //}; //// FIXME: ////assert_eq!( ////format!("{}", quote! { #parsed }), ////format!("{}", quote! { ////impl Something { ////fn something () {} ////} ////impl ::tengri::Context> for Something { ////fn get (&self, dsl: &::tengri::Value) -> Option> { ////Some(match dsl { ////::tengri::Value::Sym(":arcstr1") => "foo".into(), ////_ => return None ////}) ////} ////} ////impl ::tengri::Context>> for Something { ////fn get (&self, dsl: &::tengri::Value) -> Option>> { ////Some(match dsl { ////::tengri::Value::Sym(":optarcstr1") => Some("bar".into()), ////::tengri::Value::Sym(":optarcstr2") => Some("baz".into()), ////_ => return None ////}) ////} ////} ////impl ::tengri::Context for Something { ////fn get (&self, dsl: &::tengri::Value) -> Option { ////Some(match dsl { ////::tengri::Value::Sym(":true") => true, ////::tengri::Value::Sym(":false") => false, ////::tengri::Value::Sym(":bool1") => true || false, ////_ => return None ////}) ////} ////} ////impl ::tengri::Context for Something { ////fn get (&self, dsl: &::tengri::Value) -> Option { ////Some(match dsl { ////::tengri::Value::Num(n) => *n as u16, ////::tengri::Value::Sym(":u161") => 0 + 1, ////_ => return None ////}) ////} ////} ////impl ::tengri::Context for Something { ////fn get (&self, dsl: &::tengri::Value) -> Option { ////Some(match dsl { ////::tengri::Value::Num(n) => *n as usize, ////::tengri::Value::Sym(":usize1") => 1 + 2, ////_ => return None ////}) ////} ////} ////}) ////) //}