Building some more necessary structures out of the protocol definition

This commit is contained in:
2024-06-20 11:34:04 +02:00
parent 0125605468
commit 6c75996096
6 changed files with 225 additions and 6 deletions

View File

@@ -16,7 +16,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use proc_macro::TokenStream;
use quote::quote;
use quote::{format_ident, quote};
use syn::{parse_macro_input, spanned::Spanned, DeriveInput, Field, Ident};
#[proc_macro_derive(Protocol)]
@@ -35,7 +35,11 @@ pub fn derive_protocol(input: TokenStream) -> TokenStream {
let vis = &input.vis;
let mut server_trait = Vec::new();
let mut server_enum = Vec::new();
let mut client_impl = Vec::new();
let mut client_enum = Vec::new();
let mut query_enum = Vec::new();
for variant in &enum_.variants {
// Every variant must have 2 fields
@@ -55,32 +59,81 @@ pub fn derive_protocol(input: TokenStream) -> TokenStream {
let question_args = field_to_args(question_field);
let answer_type = variant_fields.next().unwrap().ty.clone();
// The variants that either the server or the client will use
// The "server" enum contains messages the server can send, the "client" enum contains messages the client can send
server_enum.push(quote! {
#var_name(#answer_type)
});
client_enum.push(quote! {
#var_name(#question_field)
});
// The function that the server needs to implement
server_trait.push(quote! {
fn #var_name(&mut self, #question_args) -> #answer_type;
});
// The function that the client uses to communicate
client_impl.push(quote! {
pub fn #var_name(&mut self, #question_args) -> #answer_type {
::std::unimplemented!()
}
})
});
// The query enum is the same as the source enum, but the second field is always wrapped in a Option<>
query_enum.push(quote! {
#var_name(#question_field, Option<#answer_type>)
});
}
// Create enums for the types of messages the server and client will use
let server_enum_name = format_ident!("{}Answer", name);
let server_enum = quote! {
#vis enum #server_enum_name {
#(#server_enum), *
}
};
let client_enum_name = format_ident!("{}Question", name);
let client_enum = quote! {
#vis enum #client_enum_name {
#(#client_enum), *
}
};
// Create an enum to represent the queries the client has sent
let query_enum_name = format_ident!("{}Query", name);
let query_enum = quote! {
#vis enum #query_enum_name {
#(#query_enum), *
}
};
// Create a trait which the server will have to implement
let server_trait_name = Ident::new(&format!("{}Server", name), name.span());
let server_trait_name = format_ident!("{}Server", name);
let server_trait = quote! {
#vis trait #server_trait_name {
#(#server_trait)*
}
};
let client_struct_name = Ident::new(&format!("{}Client", name), name.span());
// Create a struct which the client will use to communicate
let client_struct_name = format_ident!("{}Client", name);
let client_struct = quote! {
#vis struct #client_struct_name; // TODO: This struct will have some fields to handle the actual connection
#vis struct #client_struct_name {
queries: ::std::collections::HashMap<u64, #query_enum_name>,
send_queue: tokio::sync::mpsc::Sender<#client_enum_name>,
recv_queue: tokio::sync::mpsc::Receiver<#server_enum_name>,
} // TODO: This struct will have some fields to handle the actual connection
impl #client_struct_name {
pub fn new(send_queue: tokio::sync::mpsc::Sender<#client_enum_name>, recv_queue: tokio::sync::mpsc::Receiver<#server_enum_name>) -> Self {
Self {
queries: ::std::collections::HashMap::new(),
send_queue,
recv_queue,
}
}
#(#client_impl)*
}
};
let expanded = quote! {
#server_enum
#client_enum
#query_enum
#server_trait
#client_struct
};