rust-nex/macros/src/protos.rs

173 lines
5.5 KiB
Rust
Raw Normal View History

2025-03-23 10:54:01 +01:00
use proc_macro2::{Ident, Span, TokenStream, TokenTree};
use quote::{quote, ToTokens};
use syn::{LitInt, Token, Type};
use syn::token::{Brace, Paren, Semi};
pub struct ProtoMethodData{
pub id: LitInt,
pub name: Ident,
pub parameters: Vec<(Ident, Type)>
}
/// This is a representation of the code generated by `rmc_proto` it serves to split the logic of
/// acquiring data from the actual generation to tidy up the process into first getting then
/// generating.
///
/// Use the [`ToTokens`] trait to generate the actual code.
pub struct RmcProtocolData{
pub has_returns: bool,
pub id: LitInt,
pub name: Ident,
pub methods: Vec<ProtoMethodData>
}
impl ToTokens for RmcProtocolData{
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self{
has_returns,
name,
id,
methods
} = self;
// this gives us the name which the identifier of the corresponding Raw trait
let raw_name = Ident::new(&format!("Raw{}", name), name.span());
// boilerplate tokens which all raw traits need
quote!{
#[doc(hidden)]
pub trait #raw_name: #name
}.to_tokens(tokens);
// generate the body of the raw protocol trait
Brace::default().surround(tokens, |tokens|{
//generate each raw method
for method in methods{
let ProtoMethodData {
name,
parameters,
..
} = method;
let raw_name = Ident::new(&format!("raw_{}", name), name.span());
quote!{
async fn #raw_name
}.to_tokens(tokens);
Paren::default().surround(tokens, |tokens|{
quote!{ &self, data: ::std::vec::Vec<u8> }.to_tokens(tokens);
});
quote!{
-> ::core::result::Result<Vec<u8>, ErrorCode>
}.to_tokens(tokens);
Brace::default().surround(tokens, |tokens|{
quote! { let mut cursor = ::std::io::Cursor::new(data); }.to_tokens(tokens);
for (param_name, param_type) in parameters{
quote!{
let Ok(#param_name) =
<#param_type as crate::rmc::structures::RmcSerialize>::deserialize(
&mut cursor
) else {
return Err(ErrorCode::Core_InvalidArgument);
};
}.to_tokens(tokens)
}
quote!{
let retval = self.#name
}.to_tokens(tokens);
Paren::default().surround(tokens, |tokens|{
for (paren_name, _) in parameters{
quote!{#paren_name,}.to_tokens(tokens);
}
});
quote!{
.await;
}.to_tokens(tokens);
if *has_returns{
quote!{
let retval = retval?;
let mut vec = Vec::new();
crate::rmc::structures::RmcSerialize::serialize(&retval, &mut vec).ok();
Ok(vec)
}.to_tokens(tokens);
}
})
}
quote!{
async fn rmc_call_proto(
&self,
remote_response_connection: &crate::prudp::socket::SendingConnection,
method_id: u32,
call_id: u32,
data: Vec<u8>,
)
}.to_tokens(tokens);
Brace::default().surround(tokens, |tokens|{
quote! {
let ret = match method_id
}.to_tokens(tokens);
Brace::default().surround(tokens, |tokens|{
for method in methods{
let ProtoMethodData{
id,
name,
..
} = method;
let raw_name = Ident::new(&format!("raw_{}", name), name.span());
quote!{
#id => self.#raw_name(data).await,
}.to_tokens(tokens);
}
quote!{
_ => Err(crate::rmc::response::ErrorCode::Core_NotImplemented)
}.to_tokens(tokens);
});
Semi::default().to_tokens(tokens);
if *has_returns{
quote!{
crate::rmc::response::send_result(
remote_response_connection,
ret,
#id,
method_id,
call_id,
).await
}.to_tokens(tokens);
}
});
});
quote!{
impl<T: #name> RawAuth for T{}
}.to_tokens(tokens);
let raw_info_name = Ident::new(&format!("Raw{}Info", name), Span::call_site());
quote!{
#[doc(hidden)]
pub struct #raw_info_name;
impl #raw_info_name {
pub const PROTOCOL_ID: u16 = #id;
}
}.to_tokens(tokens);
}
}