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 } 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 }.to_tokens(tokens); }); quote!{ -> ::core::result::Result, 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, ) }.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 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); } }