Building some more necessary structures out of the protocol definition

This commit is contained in:
Kodi Craft 2024-06-20 11:34:04 +02:00
parent 0125605468
commit 6c75996096
Signed by: kodi
GPG Key ID: 69D9EED60B242822
6 changed files with 225 additions and 6 deletions

101
Cargo.lock generated
View File

@ -2,6 +2,48 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 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]] [[package]]
name = "eagle" name = "eagle"
version = "0.1.0" version = "0.1.0"
@ -9,8 +51,51 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "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]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.85" version = "1.0.85"
@ -29,6 +114,12 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.66" version = "2.0.66"
@ -40,6 +131,16 @@ dependencies = [
"unicode-ident", "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]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.12" version = "1.0.12"

View File

@ -8,6 +8,11 @@ license = "AGPL-3.0"
proc-macro2 = "1.0.85" proc-macro2 = "1.0.85"
quote = "1.0.36" quote = "1.0.36"
syn = "2.0.66" syn = "2.0.66"
tokio = { version = "1.38.0", features = ["sync"] }
[lib] [lib]
proc-macro = true proc-macro = true
[[test]]
name = "tests"
path = "tests/mod.rs"

View File

@ -32,7 +32,7 @@
inherit inputs pkgs; inherit inputs pkgs;
modules = [ modules = [
{ {
packages = with pkgs; [pkg-config]; packages = with pkgs; [pkg-config cargo-expand];
languages.rust.enable = true; languages.rust.enable = true;
languages.rust.channel = "nightly"; languages.rust.channel = "nightly";

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/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
use proc_macro::TokenStream; use proc_macro::TokenStream;
use quote::quote; use quote::{format_ident, quote};
use syn::{parse_macro_input, spanned::Spanned, DeriveInput, Field, Ident}; use syn::{parse_macro_input, spanned::Spanned, DeriveInput, Field, Ident};
#[proc_macro_derive(Protocol)] #[proc_macro_derive(Protocol)]
@ -35,7 +35,11 @@ pub fn derive_protocol(input: TokenStream) -> TokenStream {
let vis = &input.vis; let vis = &input.vis;
let mut server_trait = Vec::new(); let mut server_trait = Vec::new();
let mut server_enum = Vec::new();
let mut client_impl = 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 { for variant in &enum_.variants {
// Every variant must have 2 fields // 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 question_args = field_to_args(question_field);
let answer_type = variant_fields.next().unwrap().ty.clone(); 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! { server_trait.push(quote! {
fn #var_name(&mut self, #question_args) -> #answer_type; fn #var_name(&mut self, #question_args) -> #answer_type;
}); });
// The function that the client uses to communicate
client_impl.push(quote! { client_impl.push(quote! {
pub fn #var_name(&mut self, #question_args) -> #answer_type { pub fn #var_name(&mut self, #question_args) -> #answer_type {
::std::unimplemented!() ::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 // 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! { let server_trait = quote! {
#vis trait #server_trait_name { #vis trait #server_trait_name {
#(#server_trait)* #(#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! { 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 { 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)* #(#client_impl)*
} }
}; };
let expanded = quote! { let expanded = quote! {
#server_enum
#client_enum
#query_enum
#server_trait #server_trait
#client_struct #client_struct
}; };

58
tests/expanded.rs Normal file
View File

@ -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<i32>),
some_kind_of_question(String, Option<i32>),
}
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<u64, TestProtocolQuery>,
send_queue: tokio::sync::mpsc::Sender<TestProtocolQuestion>,
recv_queue: tokio::sync::mpsc::Receiver<TestProtocolAnswer>,
}
impl TestProtocolClient {
pub fn new(
send_queue: tokio::sync::mpsc::Sender<TestProtocolQuestion>,
recv_queue: tokio::sync::mpsc::Receiver<TestProtocolAnswer>,
) -> 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")
}
}

View File

@ -33,3 +33,5 @@ impl TestProtocolServer for DummyServer {
todo!() todo!()
} }
} }
fn main() {}