Building some more necessary structures out of the protocol definition
This commit is contained in:
parent
0125605468
commit
6c75996096
101
Cargo.lock
generated
101
Cargo.lock
generated
@ -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"
|
||||||
|
@ -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"
|
@ -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";
|
||||||
|
63
src/lib.rs
63
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 <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
58
tests/expanded.rs
Normal 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")
|
||||||
|
}
|
||||||
|
}
|
@ -33,3 +33,5 @@ impl TestProtocolServer for DummyServer {
|
|||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
Loading…
Reference in New Issue
Block a user