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() {}