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 ExposeArm(String, Ident); #[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; let state = &block.self_ty; write_quote_to(out, quote! { #block }); for (t, variants) in exposed.iter() { let formatted_type = format!("{}", quote! { #t }); let predefined = match formatted_type.as_str() { "bool" => quote! { Some(::tengri::dsl::Value::Sym(":true")) => true, Some(::tengri::dsl::Value::Sym(":false")) => false, }, "u8" | "u16" | "u32" | "u64" | "usize" | "i8" | "i16" | "i32" | "i64" | "isize" => { let num_err = LitStr::new( &format!("{{n}}: failed to convert to {formatted_type}"), Span::call_site() ); quote! { Some(::tengri::dsl::Value::Num(n)) => TryInto::<#t>::try_into(n) .unwrap_or_else(|_|panic!(#num_err)), } }, _ => quote! {}, }; let values = variants.iter().map(ExposeArm::from); write_quote_to(out, quote! { /// Generated by [tengri_proc::expose]. impl<'n> ::tengri::dsl::Take<'n, #state> for #t { fn take <'source> ( state: &#state, words: &mut ::tengri::dsl::TokenIter<'source> ) -> Perhaps { Ok(Some(match words.next().map(|x|x.value) { #predefined #(#values)* _ => return Ok(None) })) } } }); } if exposed.len() > 0 { //panic!("{}", quote! {#out}); } } } impl From<(&String, &Ident)> for ExposeArm { fn from ((a, b): (&String, &Ident)) -> Self { Self(a.clone(), b.clone()) } } impl ToTokens for ExposeArm { fn to_tokens (&self, out: &mut TokenStream2) { let Self(key, value) = self; let key = LitStr::new(&key, Span::call_site()); write_quote_to(out, quote! { Some(::tengri::dsl::Value::Sym(#key)) => state.#value(), }) } } 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) } }