2025-03-23 10:54:01 +01:00
|
|
|
use proc_macro2::{Ident, Span, TokenStream, TokenTree};
|
|
|
|
|
use quote::{quote, ToTokens};
|
2025-05-12 10:28:54 +02:00
|
|
|
use syn::{LitInt, LitStr, ReturnType, Token, Type};
|
|
|
|
|
use syn::token::{Brace, Bracket, Paren, Semi};
|
2025-03-23 10:54:01 +01:00
|
|
|
|
|
|
|
|
pub struct ProtoMethodData{
|
|
|
|
|
pub id: LitInt,
|
|
|
|
|
pub name: Ident,
|
2025-03-24 23:31:25 +01:00
|
|
|
pub parameters: Vec<(Ident, Type)>,
|
|
|
|
|
pub ret_val: ReturnType,
|
2025-03-23 10:54:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// 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>
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-24 23:31:25 +01:00
|
|
|
impl RmcProtocolData{
|
|
|
|
|
fn generate_raw_trait(&self, tokens: &mut TokenStream){
|
2025-03-23 10:54:01 +01:00
|
|
|
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);
|
|
|
|
|
});
|
|
|
|
|
|
2025-05-12 10:28:54 +02:00
|
|
|
if self.has_returns {
|
|
|
|
|
quote! {
|
|
|
|
|
-> ::core::result::Result<Vec<u8>, ErrorCode>
|
|
|
|
|
}.to_tokens(tokens);
|
|
|
|
|
}
|
2025-03-23 10:54:01 +01:00
|
|
|
|
|
|
|
|
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) =
|
2025-06-13 10:10:04 +02:00
|
|
|
<#param_type as rust_nex::rmc::structures::RmcSerialize>::deserialize(
|
2025-03-23 10:54:01 +01:00
|
|
|
&mut cursor
|
2025-05-12 10:28:54 +02:00
|
|
|
) else
|
|
|
|
|
}.to_tokens(tokens);
|
|
|
|
|
|
|
|
|
|
let error_msg = LitStr::new(&format!("an error occurred whilest deserializing {}", param_name), Span::call_site());
|
|
|
|
|
|
|
|
|
|
if self.has_returns {
|
|
|
|
|
quote! {
|
|
|
|
|
{
|
|
|
|
|
log::error!(#error_msg);
|
2025-06-13 10:10:04 +02:00
|
|
|
return Err(rust_nex::rmc::response::ErrorCode::Core_InvalidArgument);
|
2025-05-12 10:28:54 +02:00
|
|
|
};
|
|
|
|
|
}.to_tokens(tokens)
|
|
|
|
|
} else {
|
|
|
|
|
quote! {
|
|
|
|
|
{
|
|
|
|
|
log::error!(#error_msg);
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
}.to_tokens(tokens)
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-23 10:54:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
2025-06-13 10:10:04 +02:00
|
|
|
rust_nex::rmc::structures::RmcSerialize::serialize(&retval, &mut vec).ok();
|
2025-03-23 10:54:01 +01:00
|
|
|
Ok(vec)
|
|
|
|
|
}.to_tokens(tokens);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
quote!{
|
|
|
|
|
async fn rmc_call_proto(
|
|
|
|
|
&self,
|
2025-06-13 10:10:04 +02:00
|
|
|
remote_response_connection: &rust_nex::util::SendingBufferConnection,
|
2025-03-23 10:54:01 +01:00
|
|
|
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());
|
|
|
|
|
|
2025-05-12 10:28:54 +02:00
|
|
|
|
2025-03-23 10:54:01 +01:00
|
|
|
quote!{
|
|
|
|
|
#id => self.#raw_name(data).await,
|
|
|
|
|
}.to_tokens(tokens);
|
|
|
|
|
}
|
|
|
|
|
quote!{
|
2025-05-12 10:28:54 +02:00
|
|
|
v =>
|
2025-03-23 10:54:01 +01:00
|
|
|
}.to_tokens(tokens);
|
2025-05-12 10:28:54 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Brace::default().surround(tokens, |tokens|{
|
|
|
|
|
quote!{
|
|
|
|
|
log::error!("(protocol {})unimplemented method id called on protocol: {}", #id, v);
|
|
|
|
|
}.to_tokens(tokens);
|
|
|
|
|
if self.has_returns {
|
|
|
|
|
quote! {
|
2025-06-13 10:10:04 +02:00
|
|
|
Err(rust_nex::rmc::response::ErrorCode::Core_NotImplemented)
|
2025-05-12 10:28:54 +02:00
|
|
|
}.to_tokens(tokens);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2025-03-23 10:54:01 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Semi::default().to_tokens(tokens);
|
|
|
|
|
|
|
|
|
|
if *has_returns{
|
|
|
|
|
quote!{
|
2025-06-13 10:10:04 +02:00
|
|
|
rust_nex::rmc::response::send_result(
|
2025-03-23 10:54:01 +01:00
|
|
|
remote_response_connection,
|
|
|
|
|
ret,
|
|
|
|
|
#id,
|
|
|
|
|
method_id,
|
|
|
|
|
call_id,
|
|
|
|
|
).await
|
|
|
|
|
}.to_tokens(tokens);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
quote!{
|
2025-05-12 10:28:54 +02:00
|
|
|
impl<T: #name> #raw_name for T{}
|
2025-03-23 10:54:01 +01:00
|
|
|
}.to_tokens(tokens);
|
2025-03-24 23:31:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn generate_raw_remote_trait(&self, tokens: &mut TokenStream) {
|
|
|
|
|
let Self {
|
|
|
|
|
has_returns,
|
|
|
|
|
name,
|
|
|
|
|
id: proto_id,
|
|
|
|
|
methods,
|
|
|
|
|
..
|
|
|
|
|
} = self;
|
|
|
|
|
|
|
|
|
|
// this gives us the name which the identifier of the corresponding Raw trait
|
|
|
|
|
let remote_name = Ident::new(&format!("Remote{}", name), name.span());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// boilerplate tokens which all raw traits need
|
|
|
|
|
quote!{
|
|
|
|
|
#[doc(hidden)]
|
2025-06-13 10:10:04 +02:00
|
|
|
pub trait #remote_name: rust_nex::rmc::protocols::HasRmcConnection
|
2025-03-24 23:31:25 +01:00
|
|
|
}.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,
|
|
|
|
|
ret_val,
|
|
|
|
|
id: method_id,
|
|
|
|
|
..
|
|
|
|
|
} = method;
|
|
|
|
|
|
|
|
|
|
quote!{
|
|
|
|
|
async fn #name
|
|
|
|
|
}.to_tokens(tokens);
|
|
|
|
|
|
|
|
|
|
Paren::default().surround(tokens, |tokens|{
|
|
|
|
|
quote!{ &self, }.to_tokens(tokens);
|
|
|
|
|
for (param_ident, param_type) in parameters{
|
|
|
|
|
quote!{ #param_ident: #param_type, }.to_tokens(tokens);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
quote!{
|
|
|
|
|
#ret_val
|
|
|
|
|
}.to_tokens(tokens);
|
|
|
|
|
|
|
|
|
|
Brace::default().surround(tokens, |tokens|{
|
|
|
|
|
quote! {
|
|
|
|
|
let mut send_data = Vec::new();
|
|
|
|
|
let mut cursor = ::std::io::Cursor::new(&mut send_data);
|
|
|
|
|
}.to_tokens(tokens);
|
|
|
|
|
|
|
|
|
|
for (param_name, param_type) in parameters{
|
|
|
|
|
quote!{
|
2025-06-13 10:10:04 +02:00
|
|
|
rust_nex::result::ResultExtension::display_err_or_some(
|
|
|
|
|
<#param_type as rust_nex::rmc::structures::RmcSerialize>::serialize(
|
2025-03-24 23:31:25 +01:00
|
|
|
&#param_name,
|
|
|
|
|
&mut cursor
|
|
|
|
|
)
|
2025-06-13 10:10:04 +02:00
|
|
|
).ok_or(rust_nex::rmc::response::ErrorCode::Core_InvalidArgument)
|
2025-05-12 10:28:54 +02:00
|
|
|
}.to_tokens(tokens);
|
|
|
|
|
if self.has_returns {
|
|
|
|
|
quote! {
|
|
|
|
|
?;
|
|
|
|
|
}.to_tokens(tokens)
|
|
|
|
|
} else {
|
|
|
|
|
quote! {
|
|
|
|
|
;
|
|
|
|
|
}.to_tokens(tokens)
|
|
|
|
|
}
|
2025-03-24 23:31:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
quote!{
|
|
|
|
|
let call_id = rand::random();
|
|
|
|
|
|
2025-06-13 10:10:04 +02:00
|
|
|
let message = rust_nex::rmc::message::RMCMessage{
|
2025-03-24 23:31:25 +01:00
|
|
|
call_id,
|
|
|
|
|
method_id: #method_id,
|
|
|
|
|
protocol_id: #proto_id,
|
|
|
|
|
rest_of_data: send_data
|
|
|
|
|
};
|
|
|
|
|
|
2025-06-13 10:10:04 +02:00
|
|
|
let rmc_conn = <Self as rust_nex::rmc::protocols::HasRmcConnection>::get_connection(self);
|
2025-03-24 23:31:25 +01:00
|
|
|
}.to_tokens(tokens);
|
|
|
|
|
|
|
|
|
|
if *has_returns{
|
|
|
|
|
quote!{
|
2025-06-13 10:10:04 +02:00
|
|
|
rust_nex::result::ResultExtension::display_err_or_some(
|
2025-03-24 23:31:25 +01:00
|
|
|
rmc_conn.make_raw_call(&message).await
|
2025-06-13 10:10:04 +02:00
|
|
|
).ok_or(rust_nex::rmc::response::ErrorCode::Core_Exception)
|
2025-03-24 23:31:25 +01:00
|
|
|
}.to_tokens(tokens);
|
|
|
|
|
} else {
|
|
|
|
|
quote!{
|
2025-06-13 10:10:04 +02:00
|
|
|
rust_nex::result::ResultExtension::display_err_or_some(
|
2025-03-24 23:31:25 +01:00
|
|
|
rmc_conn.make_raw_call_no_response(&message).await
|
|
|
|
|
);
|
|
|
|
|
}.to_tokens(tokens);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn generate_raw_info(&self, tokens: &mut TokenStream){
|
|
|
|
|
let Self{
|
|
|
|
|
name,
|
|
|
|
|
id,
|
|
|
|
|
..
|
|
|
|
|
} = self;
|
2025-03-23 10:54:01 +01:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-24 23:31:25 +01:00
|
|
|
impl ToTokens for RmcProtocolData{
|
|
|
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
|
|
|
self.generate_raw_trait(tokens);
|
|
|
|
|
self.generate_raw_info(tokens);
|
|
|
|
|
self.generate_raw_remote_trait(tokens);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|