rework Any

This commit is contained in:
Maple Nebel 2026-06-19 17:37:17 +02:00
commit b79e47d127
13 changed files with 283 additions and 89 deletions

29
Cargo.lock generated
View file

@ -861,6 +861,16 @@ dependencies = [
"hybrid-array", "hybrid-array",
] ]
[[package]]
name = "ctor"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a"
dependencies = [
"link-section",
"linktime-proc-macro",
]
[[package]] [[package]]
name = "ctutils" name = "ctutils"
version = "0.4.2" version = "0.4.2"
@ -1022,7 +1032,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.52.0", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
@ -1572,7 +1582,7 @@ dependencies = [
"libc", "libc",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"socket2 0.5.10", "socket2 0.6.3",
"tokio", "tokio",
"tower-service", "tower-service",
"tracing", "tracing",
@ -1821,6 +1831,18 @@ dependencies = [
"vcpkg", "vcpkg",
] ]
[[package]]
name = "link-section"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2b1dd6fe32e55c0fc0ea9493aa57459ca3cf4ff3c857c7d0302290150da6e4f"
[[package]]
name = "linktime-proc-macro"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c7b0a3383c2a1002d11349c92c85a666a5fb679e96c79d782cf0dbe557fd6ee"
[[package]] [[package]]
name = "litemap" name = "litemap"
version = "0.8.2" version = "0.8.2"
@ -2403,6 +2425,7 @@ dependencies = [
"bytemuck", "bytemuck",
"cfg-if", "cfg-if",
"chrono", "chrono",
"ctor",
"dotenv", "dotenv",
"futures", "futures",
"hex", "hex",
@ -3625,7 +3648,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.61.2",
] ]
[[package]] [[package]]

View file

@ -17,20 +17,16 @@ use syn::{parse_macro_input, Data, DeriveInput, Lit, LitStr};
pub fn rmc_serialize(input: TokenStream) -> TokenStream { pub fn rmc_serialize(input: TokenStream) -> TokenStream {
let derive_input = parse_macro_input!(input as DeriveInput); let derive_input = parse_macro_input!(input as DeriveInput);
let (serialize, deserialize, write_size, version) = match &derive_input.data { let (serialize, deserialize, write_size, version, rmc_struct_impl) = match &derive_input.data {
Data::Struct(s) => rmc_serialize_struct(s, &derive_input), Data::Struct(s) => rmc_serialize_struct(s, &derive_input.ident, &derive_input),
Data::Enum(e) => rmc_serialize_enum(e, &derive_input), Data::Enum(e) => rmc_serialize_enum(e, &derive_input),
Data::Union(_) => { Data::Union(_) => {
unimplemented!("serialize a union is not allowed"); unimplemented!("serializing a union is not allowed");
} }
}; };
// generate base data // generate base data
let str_name = Lit::Str(LitStr::new(
&derive_input.ident.to_string(),
derive_input.ident.span(),
));
let ident = derive_input.ident; let ident = derive_input.ident;
let write_size = if let Some(v) = write_size { let write_size = if let Some(v) = write_size {
@ -52,6 +48,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
} else { } else {
quote! {} quote! {}
}; };
let rmc_struct_impl = rmc_struct_impl.unwrap_or_default();
let tokens = quote! { let tokens = quote! {
impl rnex_core::rmc::structures::RmcSerialize for #ident{ impl rnex_core::rmc::structures::RmcSerialize for #ident{
@ -67,11 +64,8 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
#write_size #write_size
#version #version
fn name() -> &'static str{
#str_name
}
} }
#rmc_struct_impl
}; };
tokens.into() tokens.into()

View file

@ -1,8 +1,8 @@
use proc_macro2::{Literal, Span, TokenStream}; use proc_macro2::{Literal, Span, TokenStream};
use quote::quote; use quote::{quote, ToTokens};
use syn::{ use syn::{
bracketed, parse::Parse, punctuated::Punctuated, token::Bracket, DataEnum, DataStruct, bracketed, ext, parse::Parse, punctuated::Punctuated, token::Bracket, DataEnum, DataStruct,
DeriveInput, Field, Fields, Ident, Meta, Token, Variant, DeriveInput, Field, Fields, Ident, LitStr, Meta, Token, Variant,
}; };
use crate::util::fold_tokenable; use crate::util::fold_tokenable;
@ -235,14 +235,60 @@ fn generate_struct_version(attr: Option<&RmcStructAttr>) -> proc_macro2::TokenSt
} }
} }
fn gen_rmc_struct_impl(
struct_ident: &Ident,
extended_struct: Option<&Field>,
) -> proc_macro2::TokenStream {
let self_name_str_lit = LitStr::new(&struct_ident.to_string(), struct_ident.span());
let register = if let Some(extended_struct) = extended_struct {
let extended_struct_ty = &extended_struct.ty;
let ext_ty_name = LitStr::new(
&extended_struct_ty.to_token_stream().to_string(),
Span::call_site(),
);
quote! {
#[::rnex_core::ctor(unsafe)]
#[allow(nonstandard_style)]
fn register_fun() {
println!("registering {} as parent of {}", #self_name_str_lit, #ext_ty_name);
let mut wr = <#extended_struct_ty as ::rnex_core::rmc::structures::RmcStruct>::get_struct_info()
.inheritors
.write()
.expect("poisoned");
wr.push(<#struct_ident as ::rnex_core::rmc::structures::RmcStruct>::get_struct_info());
}
}
} else {
quote! {}
};
quote! {
impl ::rnex_core::rmc::structures::RmcStruct for #struct_ident {
fn get_struct_info() -> &'static ::rnex_core::rmc::structures::RmcStructInfo {
#register
static STRUCT_DATA: ::rnex_core::rmc::structures::RmcStructInfo =
::rnex_core::rmc::structures::RmcStructInfo {
inheritors: ::std::sync::RwLock::new(::std::vec::Vec::new()),
name: #self_name_str_lit,
};
&STRUCT_DATA
}
}
}
}
pub fn rmc_serialize_struct( pub fn rmc_serialize_struct(
s: &DataStruct, s: &DataStruct,
name: &Ident,
derive_input: &DeriveInput, derive_input: &DeriveInput,
) -> ( ) -> (
proc_macro2::TokenStream, proc_macro2::TokenStream,
proc_macro2::TokenStream, proc_macro2::TokenStream,
Option<proc_macro2::TokenStream>, Option<proc_macro2::TokenStream>,
Option<proc_macro2::TokenStream>, Option<proc_macro2::TokenStream>,
Option<proc_macro2::TokenStream>,
) { ) {
let struct_attr = derive_input.attrs.iter().find(|a| { let struct_attr = derive_input.attrs.iter().find(|a| {
a.path().segments.len() == 1 a.path().segments.len() == 1
@ -285,8 +331,19 @@ pub fn rmc_serialize_struct(
generate_deserialize_struct(s, extended_struct, elements, struct_attr.is_some()); generate_deserialize_struct(s, extended_struct, elements, struct_attr.is_some());
let write_size = generate_write_size_struct(s, struct_attr.is_some()); let write_size = generate_write_size_struct(s, struct_attr.is_some());
let version = generate_struct_version(struct_attr); let version = generate_struct_version(struct_attr);
let rmc_struct_impl = if struct_attr.is_some() {
Some(gen_rmc_struct_impl(name, extended_struct))
} else {
None
};
(serialize, deserialize, Some(write_size), Some(version)) (
serialize,
deserialize,
Some(write_size),
Some(version),
rmc_struct_impl,
)
} }
fn field_to_ident(field: &Field, idx: usize) -> Ident { fn field_to_ident(field: &Field, idx: usize) -> Ident {
@ -405,6 +462,7 @@ pub fn rmc_serialize_enum(
proc_macro2::TokenStream, proc_macro2::TokenStream,
Option<proc_macro2::TokenStream>, Option<proc_macro2::TokenStream>,
Option<proc_macro2::TokenStream>, Option<proc_macro2::TokenStream>,
Option<proc_macro2::TokenStream>,
) { ) {
let repr_attr = derive_input.attrs.iter().find(|a| { let repr_attr = derive_input.attrs.iter().find(|a| {
a.path().segments.len() == 1 a.path().segments.len() == 1
@ -422,5 +480,5 @@ pub fn rmc_serialize_enum(
let serialize = rmc_generate_serialize_enum(&enum_data, &ty); let serialize = rmc_generate_serialize_enum(&enum_data, &ty);
let deserialize = rmc_generate_deserialize_enum(&enum_data, &ty); let deserialize = rmc_generate_deserialize_enum(&enum_data, &ty);
(serialize, deserialize, None, None) (serialize, deserialize, None, None, None)
} }

View file

@ -36,6 +36,7 @@ sha2 = "0.10.9"
urlencoding = "2.1.3" urlencoding = "2.1.3"
futures = "0.3.32" futures = "0.3.32"
async-trait = "0.1.89" async-trait = "0.1.89"
ctor = "1.0.7"
[dev-dependencies] [dev-dependencies]
# criterion = "0.7.0" # criterion = "0.7.0"

View file

@ -9,6 +9,8 @@ pub type PID = i64;
#[cfg(not(feature = "big_pid"))] #[cfg(not(feature = "big_pid"))]
pub type PID = i32; pub type PID = i32;
pub use ctor::ctor;
extern crate self as rnex_core; extern crate self as rnex_core;
pub mod prudp; pub mod prudp;

View file

@ -54,6 +54,7 @@ use rnex_core::rmc::structures::ranking::UploadCompetitionData;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use tokio::sync::{Mutex, RwLock}; use tokio::sync::{Mutex, RwLock};
use crate::rmc::structures::matchmake::Gathering;
use crate::rmc::structures::matchmake::MatchmakeSessionSearchCriteria; use crate::rmc::structures::matchmake::MatchmakeSessionSearchCriteria;
cfg_if! { cfg_if! {
@ -419,13 +420,11 @@ impl MatchmakeExtension for User {
async fn create_matchmake_session( async fn create_matchmake_session(
&self, &self,
gathering: Any, gathering: Any<Gathering>,
message: String, message: String,
) -> Result<(u32, Vec<u8>), ErrorCode> { ) -> Result<(u32, Vec<u8>), ErrorCode> {
info!("gathering: {:?}", gathering); info!("gathering: {:?}", gathering);
let Some(Ok(session)): Option<Result<MatchmakeSession, _>> = gathering.try_get() else { let session: MatchmakeSession = gathering.try_get_as()?;
return Err(ErrorCode::Core_InvalidArgument);
};
let session = self let session = self
.create_matchmake_session_with_param(CreateMatchmakeSessionParam { .create_matchmake_session_with_param(CreateMatchmakeSessionParam {
@ -517,14 +516,10 @@ impl MatchmakeExtension for User {
async fn auto_matchmake_with_search_criteria_postpone( async fn auto_matchmake_with_search_criteria_postpone(
&self, &self,
criteria: Vec<MatchmakeSessionSearchCriteria>, criteria: Vec<MatchmakeSessionSearchCriteria>,
gathering: Any, gathering: Any<Gathering>,
join_message: String, join_message: String,
) -> Result<Any, ErrorCode> { ) -> Result<Any<Gathering>, ErrorCode> {
let session: MatchmakeSession = gathering let session: MatchmakeSession = gathering.try_get_as()?;
.try_get()
.map(|v| v.ok())
.flatten()
.ok_or(ErrorCode::Core_InvalidArgument)?;
println!("{:?}", criteria); println!("{:?}", criteria);
@ -548,7 +543,7 @@ impl MatchmakeExtension for User {
} }
impl Matchmake for User { impl Matchmake for User {
async fn find_by_single_id(&self, gid: u32) -> Result<(bool, Any), ErrorCode> { async fn find_by_single_id(&self, gid: u32) -> Result<(bool, Any<Gathering>), ErrorCode> {
let s = self.matchmake_manager.get_session(gid).await?; let s = self.matchmake_manager.get_session(gid).await?;
let s = s.lock().await; let s = s.lock().await;
Ok(( Ok((

View file

@ -5,17 +5,32 @@ use rnex_core::{
rmc::{response::ErrorCode, structures::any::Any}, rmc::{response::ErrorCode, structures::any::Any},
}; };
use crate::{kerberos::KerberosDateTime, rmc::protocols::friends_wiiu::NNAInfo}; use crate::{
kerberos::KerberosDateTime,
rmc::{protocols::friends_wiiu::NNAInfo, structures::data::Data},
};
#[derive(RmcSerialize, Debug, Clone)] #[derive(RmcSerialize, Debug, Clone)]
#[rmc_struct(0)] #[rmc_struct(0)]
pub struct NintendoCreateAccountData { pub struct NintendoCreateAccountData {
#[extends]
pub data: Data,
pub nna_info: NNAInfo, pub nna_info: NNAInfo,
pub nex_token: String, pub nex_token: String,
pub birthday: KerberosDateTime, pub birthday: KerberosDateTime,
pub unk: u64, pub unk: u64,
} }
#[derive(RmcSerialize, Debug, Clone)]
#[rmc_struct(0)]
pub struct AccountExtraInfo {
#[extends]
pub data: Data,
pub unk1: u64,
pub unk2: u32,
pub nex_token: String,
}
#[rmc_proto(25)] #[rmc_proto(25)]
pub trait AccountManagement { pub trait AccountManagement {
#[method_id(27)] #[method_id(27)]

View file

@ -5,13 +5,14 @@ use rnex_core::rmc::response::ErrorCode;
use rnex_core::PID; use rnex_core::PID;
use crate::rmc::structures::any::Any; use crate::rmc::structures::any::Any;
use crate::rmc::structures::matchmake::Gathering;
#[rmc_proto(21)] #[rmc_proto(21)]
pub trait Matchmake { pub trait Matchmake {
#[method_id(2)] #[method_id(2)]
async fn unregister_gathering(&self, gid: u32) -> Result<bool, ErrorCode>; async fn unregister_gathering(&self, gid: u32) -> Result<bool, ErrorCode>;
#[method_id(21)] #[method_id(21)]
async fn find_by_single_id(&self, gid: u32) -> Result<(bool, Any), ErrorCode>; async fn find_by_single_id(&self, gid: u32) -> Result<(bool, Any<Gathering>), ErrorCode>;
#[method_id(41)] #[method_id(41)]
async fn get_session_urls(&self, gid: u32) -> Result<Vec<StationUrl>, ErrorCode>; async fn get_session_urls(&self, gid: u32) -> Result<Vec<StationUrl>, ErrorCode>;
#[method_id(42)] #[method_id(42)]

View file

@ -6,7 +6,7 @@ use rnex_core::rmc::structures::matchmake::{
use crate::rmc::protocols::notifications::NotificationEvent; use crate::rmc::protocols::notifications::NotificationEvent;
use crate::rmc::structures::any::Any; use crate::rmc::structures::any::Any;
use crate::rmc::structures::matchmake::MatchmakeSessionSearchCriteria; use crate::rmc::structures::matchmake::{Gathering, MatchmakeSessionSearchCriteria};
#[rmc_proto(109)] #[rmc_proto(109)]
pub trait MatchmakeExtension { pub trait MatchmakeExtension {
@ -18,7 +18,7 @@ pub trait MatchmakeExtension {
#[method_id(6)] #[method_id(6)]
async fn create_matchmake_session( async fn create_matchmake_session(
&self, &self,
gathering: Any, gathering: Any<Gathering>,
message: String, message: String,
) -> Result<(u32, Vec<u8>), ErrorCode>; ) -> Result<(u32, Vec<u8>), ErrorCode>;
@ -40,9 +40,9 @@ pub trait MatchmakeExtension {
async fn auto_matchmake_with_search_criteria_postpone( async fn auto_matchmake_with_search_criteria_postpone(
&self, &self,
criteria: Vec<MatchmakeSessionSearchCriteria>, criteria: Vec<MatchmakeSessionSearchCriteria>,
gathering: Any, gathering: Any<Gathering>,
join_msg: String, join_msg: String,
) -> Result<Any, ErrorCode>; ) -> Result<Any<Gathering>, ErrorCode>;
#[method_id(30)] #[method_id(30)]
async fn join_matchmake_session_ex( async fn join_matchmake_session_ex(

View file

@ -4,6 +4,7 @@ use rnex_core::rmc::response::ErrorCode;
use rnex_core::rmc::structures::qresult::QResult; use rnex_core::rmc::structures::qresult::QResult;
use crate::rmc::structures::any::Any; use crate::rmc::structures::any::Any;
use crate::rmc::structures::data::Data;
#[rmc_proto(11)] #[rmc_proto(11)]
pub trait Secure { pub trait Secure {
@ -17,7 +18,7 @@ pub trait Secure {
async fn register_ex( async fn register_ex(
&self, &self,
station_urls: Vec<StationUrl>, station_urls: Vec<StationUrl>,
data: Any, data: Any<Data>,
) -> Result<(QResult, u32, StationUrl), ErrorCode>; ) -> Result<(QResult, u32, StationUrl), ErrorCode>;
#[method_id(7)] #[method_id(7)]
async fn replace_url(&self, target: StationUrl, dest: StationUrl) -> Result<(), ErrorCode>; async fn replace_url(&self, target: StationUrl, dest: StationUrl) -> Result<(), ErrorCode>;

View file

@ -456,6 +456,13 @@ pub enum ErrorCode {
Ess_GameSessionMaintenance = 0x00750003, Ess_GameSessionMaintenance = 0x00750003,
} }
impl From<super::structures::Error> for ErrorCode {
fn from(value: super::structures::Error) -> Self {
error!("rmc error occurred during method runtime: {}", value);
Self::Core_InvalidArgument
}
}
impl Into<u32> for ErrorCode { impl Into<u32> for ErrorCode {
fn into(self) -> u32 { fn into(self) -> u32 {
unsafe { transmute(self) } unsafe { transmute(self) }

View file

@ -1,14 +1,20 @@
use rnex_core::rmc::structures::{Result, RmcSerialize}; use rnex_core::rmc::structures::{Result, RmcSerialize};
use std::io::{Cursor, Read, Write}; use std::{
io::{Cursor, Read, Write},
marker::PhantomData,
};
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
#[derive(Debug, Default, Clone)] use crate::rmc::structures::{RmcStruct, data::Data};
pub struct Any {
#[derive(Clone, Debug)]
pub struct Any<T: RmcStruct = Data> {
pub name: String, pub name: String,
pub data: Vec<u8>, pub data: Vec<u8>,
pub phantom_data: PhantomData<T>,
} }
impl RmcSerialize for Any { impl<T: RmcStruct> RmcSerialize for Any<T> {
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> { fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
self.name.serialize(writer)?; self.name.serialize(writer)?;
@ -21,25 +27,131 @@ impl RmcSerialize for Any {
fn deserialize(mut reader: &mut (impl Read + ?Sized)) -> Result<Self> { fn deserialize(mut reader: &mut (impl Read + ?Sized)) -> Result<Self> {
let name = String::deserialize(reader)?; let name = String::deserialize(reader)?;
if !T::get_struct_info().is_inheritor(&name) {
return Err(super::Error::InheritanceError);
}
// also length ? // also length ?
let _len2: u32 = reader.read_struct(IS_BIG_ENDIAN)?; let _len2: u32 = reader.read_struct(IS_BIG_ENDIAN)?;
let data = Vec::deserialize(reader)?; let data = Vec::deserialize(reader)?;
Ok(Any { name, data }) Ok(Any {
name,
data,
phantom_data: PhantomData,
})
} }
} }
impl Any { impl<T: RmcStruct> Any<T> {
pub fn try_get<T: RmcSerialize>(&self) -> Option<Result<T>> { pub fn try_into<U: RmcStruct>(self) -> Result<Any<U>> {
if self.name != T::name() { if !U::get_struct_info().is_inheritor(&self.name) {
return None; return Err(super::Error::InheritanceError);
} }
return Some(T::deserialize(&mut Cursor::new(&self.data[..]))); Ok(Any {
data: self.data,
name: self.name,
phantom_data: PhantomData,
})
} }
pub fn new<T: RmcSerialize>(val: &T) -> Result<Self> { pub fn try_get_as<U: RmcStruct>(&self) -> Result<U> {
if !U::get_struct_info().is_inheritor(&self.name) {
return Err(super::Error::InheritanceError);
}
return U::deserialize(&mut Cursor::new(&self.data[..]));
}
pub fn new<U: RmcStruct>(val: &U) -> Result<Self> {
if !T::get_struct_info().is_inheritor(U::get_struct_info().name) {
return Err(super::Error::InheritanceError);
}
return Ok(Self { return Ok(Self {
name: T::name().to_owned(), name: U::get_struct_info().name.to_owned(),
data: val.to_data()?, data: val.to_data()?,
phantom_data: PhantomData,
}); });
} }
pub fn get(&self) -> Result<T> {
return T::deserialize(&mut Cursor::new(&self.data[..]));
}
pub fn emplace_parent<E: RmcStruct>(&mut self, parent: &E) -> Result<()> {
// validate if E is actually a parent of the contained struct
if !E::get_struct_info().is_inheritor(&self.name) {
return Err(super::Error::InheritanceError);
}
let mut cur = Cursor::new(&self.data[..]);
// skip the parent part of the struct
let _ = T::deserialize(&mut cur)?;
let end_of_parent_pos = cur.position();
let rest_of_struct = &self
.data
.get(end_of_parent_pos as usize..)
.ok_or(super::Error::OOB)?;
let mut new_data = parent.to_data()?;
new_data.extend_from_slice(&rest_of_struct);
self.data = new_data;
Ok(())
}
}
#[cfg(test)]
mod test {
use std::io::Cursor;
use macros::{RmcSerialize, rmc_struct};
use crate::rmc::structures::RmcSerialize;
use crate::rmc::structures::any::Any;
#[derive(RmcSerialize)]
#[rmc_struct(0)]
struct StructB {
valb: u32,
}
#[derive(RmcSerialize)]
#[rmc_struct(0)]
struct StructA {
#[extends]
base: StructB,
vala: u32,
}
#[derive(RmcSerialize)]
#[rmc_struct(0)]
struct Unrelated {
vala: u32,
}
#[test]
fn test() {
let initial: Any<StructA> = Any::new(&StructA {
base: StructB { valb: 10 },
vala: 11,
})
.unwrap();
let encoded = initial.to_data().unwrap();
let mut reser_b: Any<StructB> = Any::deserialize(&mut Cursor::new(&encoded[..])).unwrap();
let mut data_b: StructB = reser_b.get().unwrap();
assert_eq!(data_b.valb, 10);
data_b.valb = 20;
reser_b.emplace_parent(&data_b).unwrap();
let data_a: StructA = reser_b.try_get_as().unwrap();
assert_eq!(data_a.vala, 11);
assert_eq!(data_a.base.valb, 20);
let data: Any<StructA> = reser_b.try_into().unwrap();
let data: Any<StructB> = data.try_into().unwrap();
let fail: Result<Any<Unrelated>, _> = data.try_into();
assert!(fail.is_err());
let fail: Result<Any<Unrelated>, _> = Any::deserialize(&mut Cursor::new(&encoded[..]));
assert!(fail.is_err());
}
} }

View file

@ -1,7 +1,9 @@
use crate::rmc::structures::helpers::DummyWriter; use crate::rmc::structures::helpers::DummyWriter;
use async_trait::async_trait; use async_trait::async_trait;
use ctor::ctor;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use std::sync::RwLock;
use std::{fmt, io}; use std::{fmt, io};
use thiserror::Error; use thiserror::Error;
//ideas for the future: make a proc macro library which allows generation of struct reads //ideas for the future: make a proc macro library which allows generation of struct reads
@ -21,11 +23,15 @@ pub enum Error {
StationUrlInvalid, StationUrlInvalid,
#[error("error formatting text: {0}")] #[error("error formatting text: {0}")]
FormatError(#[from] fmt::Error), FormatError(#[from] fmt::Error),
#[error("tried to validate inheritance chain")]
InheritanceError,
#[error("uncategorized rmc error occurred: {0}")] #[error("uncategorized rmc error occurred: {0}")]
Other(Box<dyn std::error::Error + Send + Sync>), Other(Box<dyn std::error::Error + Send + Sync>),
#[error("unexpected out of bounds read/write")]
OOB,
} }
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T, E = Error> = std::result::Result<T, E>;
pub mod any; pub mod any;
pub mod buffer; pub mod buffer;
@ -68,9 +74,6 @@ pub trait RmcSerialize {
Ok(data) Ok(data)
} }
fn name() -> &'static str {
"NoNameSpecified"
}
fn version() -> Option<u8> { fn version() -> Option<u8> {
None None
} }
@ -79,21 +82,6 @@ pub trait RmcSerialize {
trait SendWrite: Send + Write {} trait SendWrite: Send + Write {}
impl<T: Send + Write> SendWrite for T {} impl<T: Send + Write> SendWrite for T {}
// beware that this trait throws away most of the optimizations which come with using
// the regular `Serialize` trait, ONLY use this when it is 100% required to have dyn
// compatibility
#[async_trait]
trait DynRmcSerialize: Send + Sync {
async fn serialize(&self, writer: &mut dyn SendWrite) -> Result<()>;
}
#[async_trait]
impl<T: RmcSerialize + Send + Sync> DynRmcSerialize for T {
async fn serialize(&self, writer: &mut dyn SendWrite) -> Result<()> {
<Self as RmcSerialize>::serialize(&self, writer)
}
}
impl RmcSerialize for () { impl RmcSerialize for () {
fn serialize(&self, _writer: &mut (impl Write + ?Sized)) -> Result<()> { fn serialize(&self, _writer: &mut (impl Write + ?Sized)) -> Result<()> {
Ok(()) Ok(())
@ -106,31 +94,28 @@ impl RmcSerialize for () {
} }
} }
trait RmcInternalAnyUnknownAs<T: RmcStruct>: AsRef<T> + DynRmcSerialize + RmcStructInstance {} pub struct RmcStructInfo {
// this may never be locked after initialization
trait RmcCastable { pub inheritors: RwLock<Vec<&'static RmcStructInfo>>,
// consumes box and returns a Box containing a Box with the requested Struct details pub name: &'static str,
fn cast_to(self: Box<Self>, destination: &RmcStructInfo) -> Box<dyn std::any::Any>;
}
struct RmcStructInfo {
inheritors: Vec<&'static RmcStructInfo>,
name: &'static str,
deserialize_abstract: fn(&mut dyn Read) -> Box<dyn std::any::Any>,
} }
impl RmcStructInfo { impl RmcStructInfo {
fn deserialze_abstract_as<T: RmcStruct>( fn is_inheritor(&self, name: &str) -> bool {
reader: &impl Read, if name == self.name {
) -> Box<dyn RmcInternalAnyUnknownAs<T>> { return true;
todo!() }
let inheritors = self.inheritors.read().expect("poisoned");
for inheritor in inheritors.iter() {
if inheritor.is_inheritor(name) {
return true;
}
}
return false;
} }
} }
trait RmcStruct { pub trait RmcStruct: RmcSerialize {
fn get_struct_info() -> &'static RmcStructInfo; fn get_struct_info() -> &'static RmcStructInfo;
} }
trait RmcStructInstance {
fn get_self_struct_info(&self) -> &'static RmcStructInfo;
}