use crate::*; #[derive(Debug, Clone)] pub(crate) struct ExposeDef(pub(crate) ExposeMeta, pub(crate) ExposeImpl); #[derive(Debug, Clone)] pub(crate) struct ExposeMeta; #[derive(Debug, Clone)] pub(crate) struct ExposeImpl(ItemImpl, BTreeMap>); #[derive(Debug, Clone)] struct ExposeSym(LitStr); #[derive(Debug, Clone)] struct ExposeType(Box); impl Parse for ExposeMeta { fn parse (_input: ParseStream) -> Result { Ok(Self) } } impl Parse for ExposeImpl { fn parse (input: ParseStream) -> Result { let block = input.parse::()?; let mut exposed: BTreeMap> = Default::default(); for item in block.items.iter() { if let ImplItem::Fn(ImplItemFn { sig: Signature { ident, output, .. }, .. }) = item { if let ReturnType::Type(_, return_type) = output { let return_type = ExposeType(return_type.clone()); if !exposed.contains_key(&return_type) { exposed.insert(return_type.clone(), Default::default()); } let values = exposed.get_mut(&return_type).unwrap(); let key = format!(":{}", AsKebabCase(format!("{}", &ident))); if values.contains_key(&key) { return Err(input.error(format!("already defined: {key}"))) } values.insert(key, ident.clone()); } else { return Err(input.error("output type must be specified")) } } } Ok(Self(block, exposed)) } } impl ToTokens for ExposeDef { fn to_tokens (&self, out: &mut TokenStream2) { let Self(_meta, data) = self; write_quote_to(out, quote! { #data }); } } impl ToTokens for ExposeImpl { fn to_tokens (&self, out: &mut TokenStream2) { let Self(block, exposed) = self; write_quote_to(out, quote! { #block }); for (t, variants) in exposed.iter() { self.expose_variants(out, t, variants); } if exposed.len() > 0 { //panic!("{}", quote! {#out}); } } } fn is_num (t: &impl AsRef) -> bool { return matches!(t.as_ref(), | "u8" | "u16" | "u32" | "u64" | "usize" | "i8" | "i16" | "i32" | "i64" | "isize") } impl ExposeImpl { fn expose_variants ( &self, out: &mut TokenStream2, t: &ExposeType, variants: &BTreeMap ) { let Self(ItemImpl { self_ty: state, .. }, ..) = self; let type_is = format!("{}", quote! { #t }); let variants = variants.iter().map(|(key, value)|{ let key = LitStr::new(&key, Span::call_site()); quote! { Some(#key) => Ok(Some(state.#value())), } }); write_quote_to(out, if &type_is == "bool" { quote! { /// Generated by [tengri_proc::expose]. impl ::tengri::dsl::FromDsl<#state> for #t { fn from_dsl (state: &#state, dsl: &impl Dsl) -> Perhaps { match dsl.word()? { Some("true") => Ok(Some(true)), Some("false") => Ok(Some(false)), _ => match dsl.word()? { #(#variants)* _ => Ok(None) } } } } } } else if is_num(&type_is) { quote! { /// Generated by [tengri_proc::expose]. impl ::tengri::dsl::FromDsl<#state> for #t { fn from_dsl (state: &#state, dsl: &impl Dsl) -> Perhaps { match dsl.num()? { Some(n) => Ok(Some(n.parse::<#t>()?)), _ => match dsl.word()? { #(#variants)* _ => Ok(None) } } } } } } else { quote! { /// Generated by [tengri_proc::expose]. impl ::tengri::dsl::FromDsl<#state> for #t { fn from_dsl (state: &#state, dsl: &impl Dsl) -> Perhaps { match dsl.word()? { #(#variants)* _ => Ok(None) } } } } }) //let arms = variants.iter().map(|(key, value)|{ //let key = LitStr::new(&key, Span::call_site()); //quote! { #key => state.#value(), } //}); //if &type_is == "bool" { //return quote! { //::tengri::dsl::Val::Sym(s) => match s.as_ref() { //":true" => true, //":false" => false, //#variants //_ => { return Ok(None) } //}, //} //} //if matches!(type_is.as_str(), //"u8" | "u16" | "u32" | "u64" | "usize" | //"i8" | "i16" | "i32" | "i64" | "isize") //{ //let num_err = LitStr::new( //&format!("{{n}}: failed to convert to {type_is}"), //Span::call_site() //); //return quote! { //::tengri::dsl::Val::Num(n) => TryInto::<#t>::try_into(n) //.unwrap_or_else(|_|panic!(#num_err)), //::tengri::dsl::Val::Sym(s) => match s.as_ref() { //#variants //_ => { return Ok(None) } //}, //} //} //let arms = quote! { //::tengri::dsl::Val::Sym(s) => match s.as_ref() { //#variants //_ => { return Ok(None) } //}, //}; //let builtins = //write_quote_to(out, quote! { ///// Generated by [tengri_proc::expose]. //impl ::tengri::dsl::FromDsl<#state> for #t { //fn from_dsl (state: &#state, dsl: &impl Dsl) -> Perhaps { //$builtins //$defined //Ok(None) //} //} //}); } } impl From for ExposeSym { fn from (this: LitStr) -> Self { Self(this) } } impl PartialOrd for ExposeSym { fn partial_cmp (&self, other: &Self) -> Option { let this = &self.0; let that = &other.0; Some(format!("{}", quote! { #this }).cmp(&format!("{}", quote! { #that }))) } } impl Ord for ExposeSym { fn cmp (&self, other: &Self) -> Ordering { let this = &self.0; let that = &other.0; format!("{}", quote! { #this }).cmp(&format!("{}", quote! { #that })) } } impl PartialEq for ExposeSym { fn eq (&self, other: &Self) -> bool { let this = &self.0; let that = &other.0; format!("{}", quote! { #this }) == format!("{}", quote! { #that }) } } impl Eq for ExposeSym {} impl From for ExposeType { fn from (this: Type) -> Self { Self(Box::new(this)) } } impl PartialOrd for ExposeType { fn partial_cmp (&self, other: &Self) -> Option { let this = &self.0; let that = &other.0; Some(format!("{}", quote! { #this }).cmp(&format!("{}", quote! { #that }))) } } impl Ord for ExposeType { fn cmp (&self, other: &Self) -> Ordering { let this = &self.0; let that = &other.0; format!("{}", quote! { #this }).cmp(&format!("{}", quote! { #that })) } } impl PartialEq for ExposeType { fn eq (&self, other: &Self) -> bool { let this = &self.0; let that = &other.0; format!("{}", quote! { #this }) == format!("{}", quote! { #that }) } } impl Eq for ExposeType {} impl ToTokens for ExposeType { fn to_tokens (&self, out: &mut TokenStream2) { self.0.to_tokens(out) } }