tengri/proc/src/proc_expose.rs
unspeaker 5a2177cc77
Some checks are pending
/ build (push) Waiting to run
dsl: give! and take! macros
2025-05-24 00:29:50 +03:00

184 lines
5.9 KiB
Rust

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<ExposeType, BTreeMap<String, Ident>>);
#[derive(Debug, Clone)]
struct ExposeArm(String, Ident);
#[derive(Debug, Clone)]
struct ExposeSym(LitStr);
#[derive(Debug, Clone)]
struct ExposeType(Box<Type>);
impl Parse for ExposeMeta {
fn parse (_input: ParseStream) -> Result<Self> {
Ok(Self)
}
}
impl Parse for ExposeImpl {
fn parse (input: ParseStream) -> Result<Self> {
let block = input.parse::<ItemImpl>()?;
let mut exposed: BTreeMap<ExposeType, BTreeMap<String, Ident>> = 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<Self> {
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<LitStr> for ExposeSym { fn from (this: LitStr) -> Self { Self(this) } }
impl PartialOrd for ExposeSym {
fn partial_cmp (&self, other: &Self) -> Option<Ordering> {
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<Type> for ExposeType { fn from (this: Type) -> Self { Self(Box::new(this)) } }
impl PartialOrd for ExposeType {
fn partial_cmp (&self, other: &Self) -> Option<Ordering> {
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)
}
}