From 6c7599609697d6fb00c5a2e97766469c9e8d442e Mon Sep 17 00:00:00 2001 From: Kodi Craft Date: Thu, 20 Jun 2024 11:34:04 +0200 Subject: [PATCH] Building some more necessary structures out of the protocol definition --- Cargo.lock | 101 ++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 5 +++ flake.nix | 2 +- src/lib.rs | 63 ++++++++++++++++++++++++++--- tests/expanded.rs | 58 ++++++++++++++++++++++++++ tests/mod.rs | 2 + 6 files changed, 225 insertions(+), 6 deletions(-) create mode 100644 tests/expanded.rs diff --git a/Cargo.lock b/Cargo.lock index 90df08e..17368c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,48 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "cc" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "eagle" version = "0.1.0" @@ -9,8 +51,51 @@ dependencies = [ "proc-macro2", "quote", "syn", + "tokio", ] +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "object" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" +dependencies = [ + "memchr", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + [[package]] name = "proc-macro2" version = "1.0.85" @@ -29,6 +114,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + [[package]] name = "syn" version = "2.0.66" @@ -40,6 +131,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tokio" +version = "1.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +dependencies = [ + "backtrace", + "pin-project-lite", +] + [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/Cargo.toml b/Cargo.toml index e9cce3a..ba27d0b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,11 @@ license = "AGPL-3.0" proc-macro2 = "1.0.85" quote = "1.0.36" syn = "2.0.66" +tokio = { version = "1.38.0", features = ["sync"] } [lib] proc-macro = true + +[[test]] +name = "tests" +path = "tests/mod.rs" \ No newline at end of file diff --git a/flake.nix b/flake.nix index 69f6bc9..b7df19d 100644 --- a/flake.nix +++ b/flake.nix @@ -32,7 +32,7 @@ inherit inputs pkgs; modules = [ { - packages = with pkgs; [pkg-config]; + packages = with pkgs; [pkg-config cargo-expand]; languages.rust.enable = true; languages.rust.channel = "nightly"; diff --git a/src/lib.rs b/src/lib.rs index ff0c6bf..fa16b06 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ 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, + 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 }; diff --git a/tests/expanded.rs b/tests/expanded.rs new file mode 100644 index 0000000..9462fe7 --- /dev/null +++ b/tests/expanded.rs @@ -0,0 +1,58 @@ +#![feature(prelude_import)] +#[prelude_import] +use std::prelude::rust_2021::*; +#[macro_use] +extern crate std; +use eagle::Protocol; +enum TestProtocol { + Addition((i32, i32), i32), + SomeKindOfQuestion(String, i32), +} +enum TestProtocolAnswer { + addition(i32), + some_kind_of_question(i32), +} +enum TestProtocolQuestion { + addition((i32, i32)), + some_kind_of_question(String), +} +enum TestProtocolQuery { + addition((i32, i32), Option), + some_kind_of_question(String, Option), +} +trait TestProtocolServer { + fn addition(&mut self, arg0: i32, arg1: i32) -> i32; + fn some_kind_of_question(&mut self, arg: String) -> i32; +} +struct TestProtocolClient { + queries: ::std::collections::HashMap, + send_queue: tokio::sync::mpsc::Sender, + recv_queue: tokio::sync::mpsc::Receiver, +} +impl TestProtocolClient { + pub fn new( + send_queue: tokio::sync::mpsc::Sender, + recv_queue: tokio::sync::mpsc::Receiver, + ) -> Self { + Self { + queries: ::std::collections::HashMap::new(), + send_queue, + recv_queue, + } + } + pub fn addition(&mut self, arg0: i32, arg1: i32) -> i32 { + panic!("not implemented") + } + pub fn some_kind_of_question(&mut self, arg: String) -> i32 { + panic!("not implemented") + } +} +struct DummyServer; +impl TestProtocolServer for DummyServer { + fn some_kind_of_question(&mut self, question: String) -> i32 { + question.len() as i32 + } + fn addition(&mut self, arg0: i32, arg1: i32) -> i32 { + panic!("not yet implemented") + } +} diff --git a/tests/mod.rs b/tests/mod.rs index 0a9f0ff..e508ae9 100644 --- a/tests/mod.rs +++ b/tests/mod.rs @@ -33,3 +33,5 @@ impl TestProtocolServer for DummyServer { todo!() } } + +fn main() {}