From 6f02339464ec072b282886cfdf33a9261fce905b Mon Sep 17 00:00:00 2001 From: DJMrTV Date: Wed, 19 Feb 2025 23:30:15 +0100 Subject: [PATCH] feat(rmc): start implementing new rmc protocol abstraction --- Cargo.lock | 51 +++++------ Cargo.toml | 1 + macros/Cargo.lock | 4 +- macros/Cargo.toml | 6 +- macros/src/lib.rs | 188 +++++++++++++++++++++++++++++++-------- src/rmc/protocols/mod.rs | 112 ++++++++++++++++++++--- 6 files changed, 284 insertions(+), 78 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25373ac..a8ac40e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,7 +66,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -77,7 +77,7 @@ checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -225,7 +225,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.96", + "syn 2.0.98", "which", ] @@ -267,7 +267,7 @@ checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -418,7 +418,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1051,7 +1051,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.98", ] [[package]] @@ -1266,7 +1266,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1302,7 +1302,7 @@ checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1339,7 +1339,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1359,7 +1359,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "version_check", "yansi", ] @@ -1390,7 +1390,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.96", + "syn 2.0.98", "tempfile", ] @@ -1404,7 +1404,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1521,7 +1521,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1633,7 +1633,7 @@ dependencies = [ "proc-macro2", "quote", "rocket_http", - "syn 2.0.96", + "syn 2.0.98", "unicode-xid", "version_check", ] @@ -1764,7 +1764,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1869,6 +1869,7 @@ dependencies = [ "macros", "md-5", "once_cell", + "paste", "prost", "rand 0.9.0-beta.3", "rc4", @@ -1921,9 +1922,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.96" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -1976,7 +1977,7 @@ checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2047,7 +2048,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2149,7 +2150,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2217,7 +2218,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2376,7 +2377,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "wasm-bindgen-shared", ] @@ -2398,7 +2399,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2666,7 +2667,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2677,7 +2678,7 @@ checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 4c86ce9..394af2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ macros = { path = "macros" } rocket = { version = "0.5.1", features = ["json", "serde_json"] } serde = { version = "1.0.217", features = ["derive"] } async-trait = "0.1.86" +paste = "1.0.15" [build-dependencies] tonic-build = "0.12.3" diff --git a/macros/Cargo.lock b/macros/Cargo.lock index b79f6d0..a82b7df 100644 --- a/macros/Cargo.lock +++ b/macros/Cargo.lock @@ -31,9 +31,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.109" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", diff --git a/macros/Cargo.toml b/macros/Cargo.toml index a8c2072..d0bb83b 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -10,7 +10,7 @@ edition = "2018" proc-macro = true [dependencies] -quote = "1" -proc-macro2 = "1.0" -syn = "1.0" +quote = "1.0.38" +proc-macro2 = "1.0.93" +syn = { version = "2.0.98", features = ["full"] } diff --git a/macros/src/lib.rs b/macros/src/lib.rs index c4a2c8b..bbd2923 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,11 +1,14 @@ extern crate proc_macro; -use proc_macro2::TokenTree; +use proc_macro2::{Ident, Literal, Span, TokenTree}; use proc_macro::TokenStream; - -use syn::{parse_macro_input, DeriveInput, Data}; -use quote::{quote, TokenStreamExt}; - +use std::iter::FromIterator; +use syn::{parse_macro_input, DeriveInput, Data, PathSegment, TraitItem, FieldsNamed, Fields, Visibility, Type, TypePath, Path, ImplItem, ImplItemConst, Expr, ExprLit, Lit}; +use quote::{quote, ToTokens, TokenStreamExt}; +use syn::parse::ParseStream; +use syn::punctuated::Punctuated; +use syn::spanned::Spanned; +use syn::Visibility::Public; /// Example of user-defined [derive mode macro][1] /// @@ -15,8 +18,8 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { let derive_input = parse_macro_input!(input as DeriveInput); let struct_attr = derive_input.attrs.iter() - .find(|a| a.path.segments.len() == 1 && - a.path.segments.first().is_some_and(|p| p.ident.to_string() == "rmc_struct")); + .find(|a| a.path().segments.len() == 1 && + a.path().segments.first().is_some_and(|p| p.ident.to_string() == "rmc_struct")); let Data::Struct(s) = derive_input.data else { panic!("rmc struct type MUST be a struct"); @@ -29,8 +32,8 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { for f in &s.fields{ if f.attrs.iter() - .any(|a| a.path.segments.len() == 1 && - a.path.segments.first().is_some_and(|p| p.ident.to_string() == "extends")){ + .any(|a| a.path().segments.len() == 1 && + a.path().segments.first().is_some_and(|p| p.ident.to_string() == "extends")){ continue; } let ident = f.ident.as_ref().unwrap(); @@ -67,8 +70,8 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { for f in &s.fields{ if f.attrs.iter() - .any(|a| a.path.segments.len() == 1 && - a.path.segments.first().is_some_and(|p| p.ident.to_string() == "extends")){ + .any(|a| a.path().segments.len() == 1 && + a.path().segments.first().is_some_and(|p| p.ident.to_string() == "extends")){ continue; } @@ -89,23 +92,12 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { // generate base with extends stuff let serialize_base_content = if let Some(attr) = struct_attr{ - let tokens = attr.tokens.clone(); - let token = tokens.into_iter().next().unwrap(); - - let version = match token { - TokenTree::Group(g) => { - match g.stream().into_iter().next().unwrap(){ - TokenTree::Literal(l) => l, - _ => panic!("expected literal") - } - }, - _ => panic!("expected group") - }; + let version: Literal = attr.parse_args().expect("has to be a literal"); let pre_inner = if let Some(f) = s.fields.iter().find(|f| { f.attrs.iter() - .any(|a| a.path.segments.len() == 1 && - a.path.segments.first().is_some_and(|p| p.ident.to_string() == "extends")) + .any(|a| a.path().segments.len() == 1 && + a.path().segments.first().is_some_and(|p| p.ident.to_string() == "extends")) }){ let ident= f.ident.as_ref().unwrap(); quote! { @@ -128,22 +120,12 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { }; let deserialize_base_content = if let Some(attr) = struct_attr{ - let tokens = attr.tokens.clone(); - let token = tokens.into_iter().next().unwrap(); + let version: Literal = attr.parse_args().expect("has to be a literal"); - let version = match token { - TokenTree::Group(g) => { - match g.stream().into_iter().next().unwrap(){ - TokenTree::Literal(l) => l, - _ => panic!("expected literal") - } - }, - _ => panic!("expected group") - }; let pre_inner = if let Some(f) = s.fields.iter().find(|f| { f.attrs.iter() - .any(|a| a.path.segments.len() == 1 && - a.path.segments.first().is_some_and(|p| p.ident.to_string() == "extends")) + .any(|a| a.path().segments.len() == 1 && + a.path().segments.first().is_some_and(|p| p.ident.to_string() == "extends")) }){ let ident= f.ident.as_ref().unwrap(); let ty= &f.ty; @@ -181,4 +163,134 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { }; tokens.into() +} + +#[proc_macro_attribute] +pub fn rmc_proto(attr: TokenStream, input: TokenStream) -> TokenStream{ + let mut proto_num = parse_macro_input!(attr as syn::LitInt); + + let mut input = parse_macro_input!(input as syn::ItemTrait); + + let info_struct_ident = format!("Raw{}Info",input.ident.to_string()); + let info_struct_ident = Ident::new(&info_struct_ident, input.ident.span()); + + let raw_details_struct = syn::ItemStruct{ + vis: Visibility::Public(Default::default()), + struct_token: Default::default(), + fields: Fields::Unit, + semi_token: Some(Default::default()), + ident: info_struct_ident.clone(), + attrs: vec![], + generics: Default::default() + }; + + let raw_details_impl_block = syn::ItemImpl{ + impl_token: Default::default(), + generics: Default::default(), + attrs: vec![], + brace_token: Default::default(), + defaultness: None, + trait_: None, + self_ty: Box::new(Type::Path(TypePath{ + qself: None, + path: Path{ + segments: { + let mut punc = Punctuated::new(); + punc.push(PathSegment::from(info_struct_ident)); + punc + }, + leading_colon: None, + } + })), + unsafety: None, + items: vec![ + ImplItem::Const( + ImplItemConst{ + defaultness: None, + semi_token: Default::default(), + attrs: vec![], + generics: Default::default(), + ident: Ident::new("PROTOCOL_ID", Span::call_site()), + vis: Public(Default::default()), + colon_token: Default::default(), + const_token: Default::default(), + eq_token: Default::default(), + expr: Expr::Lit(ExprLit{ + attrs: vec![], + lit: Lit::Int(proto_num), + }), + ty: Type::Path(TypePath{ + qself: None, + path: Path{ + segments: { + let mut punc = Punctuated::new(); + punc.push(PathSegment::from(Ident::new("u16", Span::call_site()))); + punc + }, + leading_colon: None, + } + }) + } + ) + ] + }; + + let funcs = input.items.iter().filter_map(|v| if let TraitItem::Fn(v) = v {Some(v)} else { None }); + + for func in funcs{ + if matches!(func.default, Some(_)){ + return syn::Error::new(func.default.span(), "rmc methods may not have bodies").to_compile_error().into(); + } + + let Some(attr) = func.attrs.iter() + .find(|a| a.path().segments.last().is_some_and(|s| s.ident.to_string() == "method_id")) else { + let span = func.sig.asyncness.span().join(func.semi_token.unwrap().span()).unwrap_or(func.sig.span()); + return syn::Error::new(span, "every function inside of an rmc protocol must have a method id").to_compile_error().into(); + }; + + todo!("generate raw impl") + } + + quote!{ + #input + #raw_details_struct + #raw_details_impl_block + }.into() + +} + + +#[proc_macro_attribute] +pub fn method_id(_attr: TokenStream, input: TokenStream) -> TokenStream{ + // this attribute doesnt do anything by itself, see `rmc_proto` + input +} + + +#[proc_macro_attribute] +pub fn rmc_struct(attr: TokenStream, input: TokenStream) -> TokenStream{ + let mut type_data = parse_macro_input!(input as DeriveInput); + let mut ident = parse_macro_input!(attr as syn::Path); + let last_token = ident.segments.last_mut().expect("empty path?"); + + last_token.ident = Ident::new(&("Local".to_owned() + &last_token.ident.to_string()), last_token.span()); + + + let struct_name = &type_data.ident; + + let out = quote!{ + #type_data + + impl #ident for #struct_name{ + + } + + impl crate::rmc::protocols::RmcCallable for #struct_name{ + async fn rmc_call(&self, protocol_id: u16, method_id: u32, rest: Vec){ + ::rmc_call(self, protocol_id, method_id, rest).await; + } + } + }; + + out.into() } \ No newline at end of file diff --git a/src/rmc/protocols/mod.rs b/src/rmc/protocols/mod.rs index 2762de2..5025cb4 100644 --- a/src/rmc/protocols/mod.rs +++ b/src/rmc/protocols/mod.rs @@ -1,17 +1,109 @@ -use std::sync::Arc; +use macros::method_id; +use std::collections::HashMap; +use std::ops::Add; +use std::sync::{Arc, Condvar}; +use std::time::Duration; use async_trait::async_trait; -use tokio::sync::Mutex; -use crate::prudp::socket::ExternalConnection; +use chrono::TimeDelta; +use macros::{rmc_proto, rmc_struct}; +use paste::paste; +use tokio::sync::{Mutex, Notify}; +use tokio::time::{sleep_until, Instant}; +use crate::prudp::socket::{ExternalConnection, SendingConnection}; use crate::rmc::structures::connection_data::ConnectionData; -pub trait RmcCallable{ - fn rmc_call(protocol_id: u8, method_id: u8, rest: Vec); -} +struct RmcConnection(SendingConnection, RmcResponseReceiver); -struct LocalRmcObjectWrapper(T); -impl LocalRmcObjectWrapper{ - pub fn new(object: T, conn: ExternalConnection) -> Self{ - unimplemented!() +struct RmcResponseReceiver(Notify, Mutex>>); + +impl RmcResponseReceiver{ + // returns none if timed out + pub async fn get_response_data(&self, proto: u16, method: u32) -> Option>{ + let mut end_wait_time = Instant::now(); + end_wait_time += Duration::from_secs(5); + + let sleep_fut = sleep_until(end_wait_time); + tokio::pin!(sleep_fut); + + loop { + let mut locked = self.1.lock().await; + + if let Some(v) = locked.remove(&(proto, method)){ + return Some(v); + } + + let notif_fut = self.0.notified(); + + drop(locked); + + tokio::select! { + _ = &mut sleep_fut => { + return None; + } + _ = notif_fut => { + continue; + } + } + } } } +pub trait RmcCallable{ + async fn rmc_call(&self, protocol_id: u16, method_id: u32, rest: Vec); +} + + +macro_rules! define_rmc_proto { + (proto $name:ident{ + $($protocol:path),* + }) => { + paste!{ + trait []: std::any::Any $( + [])* { + async fn rmc_call(&self, protocol_id: u16, method_id: u32, rest: Vec){ + match protocol_id{ + $( + []::PROTOCOL_ID => ]>::rmc_call_proto(self, method_id, rest).await, + )* + v => log::error!("invalid protocol called on rmc object {}", v) + } + } + } + } + }; +} + +trait RawNotif{ + async fn rmc_call_proto(&self, method_id: u32, rest: Vec){ + + } +} + +struct RawNotifInfo; +impl RawNotifInfo{ + const PROTOCOL_ID: u16 = 10; +} + +#[rmc_proto(1)] +pub trait Another{ + #[method_id(1)] + async fn test(); +} + +define_rmc_proto!{ + proto TestProto{ + Notif, + Another + } +} + + + +#[rmc_struct(TestProto)] +struct TestProtoImplementor{ + +} + +impl RawNotif for TestProtoImplementor{ + +} +