mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2025-12-06 11:46:42 +01:00
184 lines
5.9 KiB
Rust
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)
|
|
}
|
|
}
|