redo macros
All checks were successful
Build and Test / splatoon-testfire (push) Successful in 4m29s
Build and Test / puyopuyo (push) Successful in 5m3s
Build and Test / splatoon (push) Successful in 5m40s
Build and Test / wii-sports-club (push) Successful in 5m46s
Build and Test / fast-racing-neo (push) Successful in 6m30s
Build and Test / wii-u-chat (push) Successful in 7m18s
Build and Test / friends (push) Successful in 8m19s
Build and Test / super-mario-maker (push) Successful in 13m10s
Build and Test / mario-tennis (push) Successful in 13m34s
Build and Test / minecraft-wiiu (push) Successful in 14m8s
All checks were successful
Build and Test / splatoon-testfire (push) Successful in 4m29s
Build and Test / puyopuyo (push) Successful in 5m3s
Build and Test / splatoon (push) Successful in 5m40s
Build and Test / wii-sports-club (push) Successful in 5m46s
Build and Test / fast-racing-neo (push) Successful in 6m30s
Build and Test / wii-u-chat (push) Successful in 7m18s
Build and Test / friends (push) Successful in 8m19s
Build and Test / super-mario-maker (push) Successful in 13m10s
Build and Test / mario-tennis (push) Successful in 13m34s
Build and Test / minecraft-wiiu (push) Successful in 14m8s
This commit is contained in:
parent
d10e0cb596
commit
de20212d1e
15 changed files with 1189 additions and 966 deletions
531
Cargo.lock
generated
531
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,400 +1,27 @@
|
||||||
|
#![allow(dead_code)]
|
||||||
mod protos;
|
mod protos;
|
||||||
|
mod rmc_struct;
|
||||||
|
mod util;
|
||||||
|
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
|
|
||||||
use crate::protos::{ProtoMethodData, RmcProtocolData};
|
use crate::protos::{ProtoInputParams, RmcProtocolData};
|
||||||
|
use crate::rmc_struct::{rmc_serialize_enum, rmc_serialize_struct};
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro2::{Ident, Literal, Span};
|
use proc_macro2::Ident;
|
||||||
use quote::{quote, TokenStreamExt};
|
use quote::quote;
|
||||||
use syn::parse::{Parse, ParseStream};
|
|
||||||
use syn::punctuated::Punctuated;
|
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::{
|
use syn::{parse_macro_input, Data, DeriveInput, Lit, LitStr};
|
||||||
parse_macro_input, Attribute, Data, DataStruct, DeriveInput, Fields, FnArg, Lit, LitInt,
|
|
||||||
LitStr, Pat, Token, TraitItem,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ProtoInputParams {
|
|
||||||
proto_num: LitInt,
|
|
||||||
properties: Option<(Token![,], Punctuated<Ident, Token![,]>)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse for ProtoInputParams {
|
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
|
||||||
let proto_num = input.parse()?;
|
|
||||||
|
|
||||||
if let Some(seperator) = input.parse()? {
|
|
||||||
let mut punctuated = Punctuated::new();
|
|
||||||
loop {
|
|
||||||
punctuated.push_value(input.parse()?);
|
|
||||||
if let Some(punct) = input.parse()? {
|
|
||||||
punctuated.push_punct(punct);
|
|
||||||
} else {
|
|
||||||
return Ok(Self {
|
|
||||||
proto_num,
|
|
||||||
properties: Some((seperator, punctuated)),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(Self {
|
|
||||||
proto_num,
|
|
||||||
properties: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gen_serialize_data_struct(
|
|
||||||
s: DataStruct,
|
|
||||||
struct_attr: Option<&Attribute>,
|
|
||||||
) -> (
|
|
||||||
proc_macro2::TokenStream,
|
|
||||||
proc_macro2::TokenStream,
|
|
||||||
proc_macro2::TokenStream,
|
|
||||||
) {
|
|
||||||
let serialize_base_content = {
|
|
||||||
let mut serialize_content = quote! {};
|
|
||||||
|
|
||||||
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")
|
|
||||||
}) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let ident = f.ident.as_ref().unwrap();
|
|
||||||
|
|
||||||
serialize_content.append_all(quote! {
|
|
||||||
rnex_core::rmc::structures::RmcSerialize::serialize(&self.#ident, writer)?;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
#serialize_content
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let struct_ctor = {
|
|
||||||
let mut structure_content = quote! {};
|
|
||||||
for f in &s.fields {
|
|
||||||
let ident = f.ident.as_ref().unwrap();
|
|
||||||
|
|
||||||
structure_content.append_all(quote! {#ident, });
|
|
||||||
}
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
Ok(Self{
|
|
||||||
#structure_content
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let deserialize_base_content = {
|
|
||||||
let mut deserialize_content = quote! {};
|
|
||||||
|
|
||||||
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")
|
|
||||||
}) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ident = f.ident.as_ref().unwrap();
|
|
||||||
let ty = &f.ty;
|
|
||||||
|
|
||||||
deserialize_content.append_all(quote! {
|
|
||||||
let #ident = <#ty> :: deserialize(reader)?;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
#deserialize_content
|
|
||||||
#struct_ctor
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let write_size = {
|
|
||||||
let mut size_content = quote! { 0 };
|
|
||||||
|
|
||||||
for f in &s.fields {
|
|
||||||
let ident = f.ident.as_ref().unwrap();
|
|
||||||
|
|
||||||
size_content.append_all(quote! {
|
|
||||||
+ rnex_core::rmc::structures::RmcSerialize::serialize_write_size(&self.#ident)?
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
size_content
|
|
||||||
};
|
|
||||||
let write_size = if let Some(_) = struct_attr {
|
|
||||||
quote! { #write_size + if rnex_core::config::FEATURE_HAS_STRUCT_HEADER{ 5 } else { 0 } }
|
|
||||||
} else {
|
|
||||||
write_size
|
|
||||||
};
|
|
||||||
|
|
||||||
// generate base with extends stuff
|
|
||||||
|
|
||||||
let serialize_base_content = if let Some(attr) = struct_attr {
|
|
||||||
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")
|
|
||||||
})
|
|
||||||
}) {
|
|
||||||
let ident = f.ident.as_ref().unwrap();
|
|
||||||
quote! {
|
|
||||||
self.#ident.serialize(writer)?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote! {}
|
|
||||||
};
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
#pre_inner
|
|
||||||
rnex_core::rmc::structures::rmc_struct::write_struct(
|
|
||||||
writer,
|
|
||||||
#version,
|
|
||||||
rnex_core::rmc::structures::helpers::len_of_write(
|
|
||||||
|writer|{
|
|
||||||
#serialize_base_content
|
|
||||||
}
|
|
||||||
),
|
|
||||||
|writer|{
|
|
||||||
#serialize_base_content
|
|
||||||
}
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
serialize_base_content
|
|
||||||
};
|
|
||||||
|
|
||||||
let deserialize_base_content = if let Some(attr) = struct_attr {
|
|
||||||
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")
|
|
||||||
})
|
|
||||||
}) {
|
|
||||||
let ident = f.ident.as_ref().unwrap();
|
|
||||||
let ty = &f.ty;
|
|
||||||
quote! {
|
|
||||||
let #ident = <#ty> :: deserialize(reader)?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote! {}
|
|
||||||
};
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
#pre_inner
|
|
||||||
Ok(rnex_core::rmc::structures::rmc_struct::read_struct(reader, #version, move |mut reader|{
|
|
||||||
#deserialize_base_content
|
|
||||||
})?)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
deserialize_base_content
|
|
||||||
};
|
|
||||||
|
|
||||||
let write_size = quote! {
|
|
||||||
fn serialize_write_size(&self) -> rnex_core::rmc::structures::Result<u32>{
|
|
||||||
Ok(#write_size)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
(serialize_base_content, deserialize_base_content, write_size)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[proc_macro_derive(RmcSerialize, attributes(extends, rmc_struct))]
|
#[proc_macro_derive(RmcSerialize, attributes(extends, rmc_struct))]
|
||||||
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 struct_attr = derive_input.attrs.iter().find(|a| {
|
let (serialize, deserialize, write_size, version) = match &derive_input.data {
|
||||||
a.path().segments.len() == 1
|
Data::Struct(s) => rmc_serialize_struct(s, &derive_input),
|
||||||
&& a.path()
|
Data::Enum(e) => rmc_serialize_enum(e, &derive_input),
|
||||||
.segments
|
|
||||||
.first()
|
|
||||||
.is_some_and(|p| p.ident.to_string() == "rmc_struct")
|
|
||||||
});
|
|
||||||
let repr_attr = derive_input.attrs.iter().find(|a| {
|
|
||||||
a.path().segments.len() == 1
|
|
||||||
&& a.path()
|
|
||||||
.segments
|
|
||||||
.first()
|
|
||||||
.is_some_and(|p| p.ident.to_string() == "repr")
|
|
||||||
});
|
|
||||||
|
|
||||||
/*let Data::Struct(s) = derive_input.data else {
|
|
||||||
panic!("rmc struct type MUST be a struct");
|
|
||||||
};*/
|
|
||||||
|
|
||||||
let (serialize_base_content, deserialize_base_content, write_size) = match derive_input.data {
|
|
||||||
Data::Struct(s) => gen_serialize_data_struct(s, struct_attr),
|
|
||||||
Data::Enum(e) => {
|
|
||||||
let Some(repr_attr) = repr_attr else {
|
|
||||||
panic!("missing repr attribute");
|
|
||||||
};
|
|
||||||
|
|
||||||
let ty: Ident = repr_attr.parse_args().unwrap();
|
|
||||||
|
|
||||||
let mut inner_match_de = quote! {};
|
|
||||||
let mut inner_match_se = quote! {};
|
|
||||||
//let mut inner_match_len = quote!{};
|
|
||||||
|
|
||||||
for variant in e.variants {
|
|
||||||
let Some((_, val)) = variant.discriminant else {
|
|
||||||
panic!("missing discriminant");
|
|
||||||
};
|
|
||||||
|
|
||||||
let field_data_de = match &variant.fields {
|
|
||||||
Fields::Named(v) => {
|
|
||||||
let mut base = quote! {};
|
|
||||||
for field in v.named.iter() {
|
|
||||||
let ty = &field.ty;
|
|
||||||
let name = &field.ident;
|
|
||||||
|
|
||||||
base.append_all(quote!{
|
|
||||||
#name: <#ty as rnex_core::rmc::structures::RmcSerialize>::deserialize(reader)?,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
quote! {{#base}}
|
|
||||||
}
|
|
||||||
Fields::Unnamed(n) => {
|
|
||||||
let mut base = quote! {};
|
|
||||||
|
|
||||||
for field in n.unnamed.iter() {
|
|
||||||
let ty = &field.ty;
|
|
||||||
|
|
||||||
base.append_all(quote!{
|
|
||||||
<#ty as rnex_core::rmc::structures::RmcSerialize>::deserialize(reader)?,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
quote! {(#base)}
|
|
||||||
}
|
|
||||||
Fields::Unit => {
|
|
||||||
quote! {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut se_with_fields = quote! {
|
|
||||||
<#ty as rnex_core::rmc::structures::RmcSerialize>::serialize(&#val, writer)?;
|
|
||||||
};
|
|
||||||
|
|
||||||
match &variant.fields {
|
|
||||||
Fields::Named(v) => {
|
|
||||||
for field in v.named.iter() {
|
|
||||||
let ty = &field.ty;
|
|
||||||
let name = &field.ident;
|
|
||||||
|
|
||||||
se_with_fields.append_all(quote!{
|
|
||||||
<#ty as rnex_core::rmc::structures::RmcSerialize>::serialize(#name ,writer)?;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Fields::Unnamed(n) => {
|
|
||||||
for (i, field) in n.unnamed.iter().enumerate() {
|
|
||||||
let ty = &field.ty;
|
|
||||||
|
|
||||||
let ident = Ident::new(&format!("val_{}", i), Span::call_site());
|
|
||||||
|
|
||||||
se_with_fields.append_all(quote!{
|
|
||||||
<#ty as rnex_core::rmc::structures::RmcSerialize>::serialize(#ident, writer)?;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Fields::Unit => {}
|
|
||||||
};
|
|
||||||
|
|
||||||
let field_match_se = match &variant.fields {
|
|
||||||
Fields::Named(v) => {
|
|
||||||
let mut base = quote! {};
|
|
||||||
|
|
||||||
for field in v.named.iter() {
|
|
||||||
let name = &field.ident;
|
|
||||||
|
|
||||||
base.append_all(quote! {
|
|
||||||
#name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
quote! {{#base}}
|
|
||||||
}
|
|
||||||
Fields::Unnamed(n) => {
|
|
||||||
let mut base = quote! {};
|
|
||||||
|
|
||||||
for (i, _field) in n.unnamed.iter().enumerate() {
|
|
||||||
let ident = Ident::new(&format!("val_{}", i), Span::call_site());
|
|
||||||
|
|
||||||
base.append_all(quote! {
|
|
||||||
#ident,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
quote! {(#base)}
|
|
||||||
}
|
|
||||||
Fields::Unit => {
|
|
||||||
quote! {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let name = variant.ident;
|
|
||||||
|
|
||||||
inner_match_de.append_all(quote! {
|
|
||||||
#val => Self::#name #field_data_de,
|
|
||||||
});
|
|
||||||
|
|
||||||
inner_match_se.append_all(quote! {
|
|
||||||
Self::#name #field_match_se => {
|
|
||||||
#se_with_fields
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let serialize_base_content = quote! {
|
|
||||||
match self{
|
|
||||||
#inner_match_se
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
|
|
||||||
let deserialize_base_content = quote! {
|
|
||||||
let val: Self = match <#ty as rnex_core::rmc::structures::RmcSerialize>::deserialize(reader)?{
|
|
||||||
#inner_match_de
|
|
||||||
v => return Err(rnex_core::rmc::structures::Error::UnexpectedValue(v as _))
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(val)
|
|
||||||
};
|
|
||||||
|
|
||||||
(serialize_base_content, deserialize_base_content, quote! {})
|
|
||||||
}
|
|
||||||
Data::Union(_) => {
|
Data::Union(_) => {
|
||||||
unimplemented!()
|
unimplemented!("serialize a union is not allowed");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -406,19 +33,41 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
|
||||||
));
|
));
|
||||||
let ident = derive_input.ident;
|
let ident = derive_input.ident;
|
||||||
|
|
||||||
|
let write_size = if let Some(v) = write_size {
|
||||||
|
quote! {
|
||||||
|
fn serialize_write_size(&self) -> rnex_core::rmc::structures::Result<u32>{
|
||||||
|
#v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {}
|
||||||
|
};
|
||||||
|
|
||||||
|
let version = if let Some(v) = version {
|
||||||
|
quote! {
|
||||||
|
fn version() -> Option<u8>{
|
||||||
|
#v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {}
|
||||||
|
};
|
||||||
|
|
||||||
let tokens = quote! {
|
let tokens = quote! {
|
||||||
impl rnex_core::rmc::structures::RmcSerialize for #ident{
|
impl rnex_core::rmc::structures::RmcSerialize for #ident{
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn serialize(&self, writer: &mut impl ::std::io::Write) -> rnex_core::rmc::structures::Result<()>{
|
fn serialize(&self, writer: &mut impl ::std::io::Write) -> rnex_core::rmc::structures::Result<()>{
|
||||||
#serialize_base_content
|
#serialize
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn deserialize(reader: &mut impl ::std::io::Read) -> rnex_core::rmc::structures::Result<Self>{
|
fn deserialize(reader: &mut impl ::std::io::Read) -> rnex_core::rmc::structures::Result<Self>{
|
||||||
#deserialize_base_content
|
#deserialize
|
||||||
}
|
}
|
||||||
|
|
||||||
#write_size
|
#write_size
|
||||||
|
|
||||||
|
#version
|
||||||
|
|
||||||
fn name() -> &'static str{
|
fn name() -> &'static str{
|
||||||
#str_name
|
#str_name
|
||||||
}
|
}
|
||||||
|
|
@ -455,71 +104,9 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn rmc_proto(attr: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn rmc_proto(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
let params = parse_macro_input!(attr as ProtoInputParams);
|
let params = parse_macro_input!(attr as ProtoInputParams);
|
||||||
|
|
||||||
let ProtoInputParams {
|
|
||||||
proto_num,
|
|
||||||
properties,
|
|
||||||
} = params;
|
|
||||||
|
|
||||||
let no_return_data =
|
|
||||||
properties.is_some_and(|p| p.1.iter().any(|i| i.to_string() == "NoReturn"));
|
|
||||||
|
|
||||||
let input = parse_macro_input!(input as syn::ItemTrait);
|
let input = parse_macro_input!(input as syn::ItemTrait);
|
||||||
|
|
||||||
// gigantic ass struct initializer (to summarize this gets all of the data)
|
let raw_data = RmcProtocolData::new(params, &input);
|
||||||
let raw_data = RmcProtocolData {
|
|
||||||
has_returns: !no_return_data,
|
|
||||||
name: input.ident.clone(),
|
|
||||||
id: proto_num,
|
|
||||||
methods: input
|
|
||||||
.items
|
|
||||||
.iter()
|
|
||||||
.filter_map(|v| match v {
|
|
||||||
TraitItem::Fn(v) => Some(v),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.map(|func| {
|
|
||||||
let Some(attr) = func.attrs.iter().find(|a| {
|
|
||||||
a.path()
|
|
||||||
.segments
|
|
||||||
.last()
|
|
||||||
.is_some_and(|s| s.ident.to_string() == "method_id")
|
|
||||||
}) else {
|
|
||||||
panic!("every function inside of an rmc protocol must have a method id");
|
|
||||||
};
|
|
||||||
|
|
||||||
let Ok(id): Result<LitInt, _> = attr.parse_args() else {
|
|
||||||
panic!("todo: put a propper error message here");
|
|
||||||
};
|
|
||||||
|
|
||||||
let funcs = func
|
|
||||||
.sig
|
|
||||||
.inputs
|
|
||||||
.iter()
|
|
||||||
.skip(1)
|
|
||||||
.map(|f| {
|
|
||||||
let FnArg::Typed(t) = f else {
|
|
||||||
panic!("what");
|
|
||||||
};
|
|
||||||
let Pat::Ident(i) = &*t.pat else {
|
|
||||||
panic!(
|
|
||||||
"unable to handle non identifier patterns as parameter bindings"
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
(i.ident.clone(), t.ty.as_ref().clone())
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
ProtoMethodData {
|
|
||||||
id,
|
|
||||||
name: func.sig.ident.clone(),
|
|
||||||
parameters: funcs,
|
|
||||||
ret_val: func.sig.output.clone(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
};
|
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#input
|
#input
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,48 @@
|
||||||
use proc_macro2::{Ident, Span, TokenStream};
|
use proc_macro2::{Ident, Span, TokenStream};
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
use syn::token::{Brace, Paren, Semi};
|
use syn::{
|
||||||
use syn::{LitInt, LitStr, ReturnType, Type};
|
parse::{Parse, ParseStream},
|
||||||
|
punctuated::Punctuated,
|
||||||
|
Attribute, FnArg, ItemTrait, LitInt, LitStr, Meta, Pat, ReturnType, Token, TraitItem, Type,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::util::fold_tokenable;
|
||||||
|
|
||||||
|
pub struct ProtoInputParams {
|
||||||
|
proto_num: LitInt,
|
||||||
|
properties: Option<(Token![,], Punctuated<Ident, Token![,]>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for ProtoInputParams {
|
||||||
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
let proto_num = input.parse()?;
|
||||||
|
|
||||||
|
if let Some(seperator) = input.parse()? {
|
||||||
|
let mut punctuated = Punctuated::new();
|
||||||
|
loop {
|
||||||
|
punctuated.push_value(input.parse()?);
|
||||||
|
if let Some(punct) = input.parse()? {
|
||||||
|
punctuated.push_punct(punct);
|
||||||
|
} else {
|
||||||
|
return Ok(Self {
|
||||||
|
proto_num,
|
||||||
|
properties: Some((seperator, punctuated)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(Self {
|
||||||
|
proto_num,
|
||||||
|
properties: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
pub struct ProtoMethodData {
|
pub struct ProtoMethodData {
|
||||||
pub id: LitInt,
|
pub id: LitInt,
|
||||||
pub name: Ident,
|
pub name: Ident,
|
||||||
pub parameters: Vec<(Ident, Type)>,
|
pub attributes: Vec<Attribute>,
|
||||||
|
pub parameters: Vec<(Ident, Type, Vec<Attribute>)>,
|
||||||
pub ret_val: ReturnType,
|
pub ret_val: ReturnType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -23,108 +59,222 @@ pub struct RmcProtocolData {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RmcProtocolData {
|
impl RmcProtocolData {
|
||||||
fn generate_raw_trait(&self, tokens: &mut TokenStream) {
|
pub fn new(params: ProtoInputParams, input: &ItemTrait) -> Self {
|
||||||
|
let ProtoInputParams {
|
||||||
|
proto_num,
|
||||||
|
properties,
|
||||||
|
} = params;
|
||||||
|
|
||||||
|
let no_return_data =
|
||||||
|
properties.is_some_and(|p| p.1.iter().any(|i| i.to_string() == "NoReturn"));
|
||||||
|
|
||||||
|
// gigantic ass struct initializer (to summarize this gets all of the data)
|
||||||
|
RmcProtocolData {
|
||||||
|
has_returns: !no_return_data,
|
||||||
|
name: input.ident.clone(),
|
||||||
|
id: proto_num,
|
||||||
|
methods: input
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.filter_map(|v| match v {
|
||||||
|
TraitItem::Fn(v) => Some(v),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.map(|func| {
|
||||||
|
let Some(attr) = func.attrs.iter().find(|a| {
|
||||||
|
a.path()
|
||||||
|
.segments
|
||||||
|
.last()
|
||||||
|
.is_some_and(|s| s.ident.to_string() == "method_id")
|
||||||
|
}) else {
|
||||||
|
panic!("every function inside of an rmc protocol must have a method id");
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(id): Result<LitInt, _> = attr.parse_args() else {
|
||||||
|
panic!("todo: put a propper error message here");
|
||||||
|
};
|
||||||
|
|
||||||
|
let funcs = func
|
||||||
|
.sig
|
||||||
|
.inputs
|
||||||
|
.iter()
|
||||||
|
.skip(1)
|
||||||
|
.map(|f| {
|
||||||
|
let FnArg::Typed(t) = f else {
|
||||||
|
panic!("what");
|
||||||
|
};
|
||||||
|
let Pat::Ident(i) = &*t.pat else {
|
||||||
|
panic!(
|
||||||
|
"unable to handle non identifier patterns as parameter bindings"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
(i.ident.clone(), t.ty.as_ref().clone(), t.attrs.clone())
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
ProtoMethodData {
|
||||||
|
id,
|
||||||
|
name: func.sig.ident.clone(),
|
||||||
|
parameters: funcs,
|
||||||
|
ret_val: func.sig.output.clone(),
|
||||||
|
attributes: func
|
||||||
|
.attrs
|
||||||
|
.iter()
|
||||||
|
.filter(|a| match &a.meta {
|
||||||
|
Meta::NameValue(v) => {
|
||||||
|
if let Some(i) = v.path.get_ident() {
|
||||||
|
i.to_string() != "doc"
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Meta::List(l) => {
|
||||||
|
if let Some(seg) = l.path.segments.last() {
|
||||||
|
seg.ident.to_string() != "method_id"
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => true,
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_raw_trait(&self) -> TokenStream {
|
||||||
let Self {
|
let Self {
|
||||||
has_returns,
|
has_returns,
|
||||||
name,
|
name,
|
||||||
id,
|
id,
|
||||||
methods,
|
methods,
|
||||||
} = self;
|
} = self;
|
||||||
|
let generate_raw_method = |method: &ProtoMethodData| -> TokenStream {
|
||||||
|
let ProtoMethodData {
|
||||||
|
name,
|
||||||
|
parameters,
|
||||||
|
attributes,
|
||||||
|
..
|
||||||
|
} = method;
|
||||||
|
|
||||||
// this gives us the name which the identifier of the corresponding Raw trait
|
let attribs = fold_tokenable(attributes.iter());
|
||||||
let raw_name = Ident::new(&format!("Raw{}", name), name.span());
|
|
||||||
|
|
||||||
// boilerplate tokens which all raw traits need
|
let raw_name = Ident::new(&format!("raw_{}", name), name.span());
|
||||||
quote! {
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[allow(unused_must_use)]
|
|
||||||
pub trait #raw_name: #name
|
|
||||||
}
|
|
||||||
.to_tokens(tokens);
|
|
||||||
|
|
||||||
// generate the body of the raw protocol trait
|
let optional_return = if self.has_returns {
|
||||||
Brace::default().surround(tokens, |tokens|{
|
quote! {
|
||||||
//generate each raw method
|
-> ::core::result::Result<Vec<u8>, ::rnex_core::rmc::response::ErrorCode>
|
||||||
for method in methods{
|
}
|
||||||
let ProtoMethodData {
|
} else {
|
||||||
name,
|
quote! {}
|
||||||
parameters,
|
}
|
||||||
..
|
.into_token_stream();
|
||||||
} = method;
|
|
||||||
|
|
||||||
let raw_name = Ident::new(&format!("raw_{}", name), name.span());
|
let deser_params =
|
||||||
quote!{
|
fold_tokenable(parameters.iter().map(|(param_name, param_type, attribs)| {
|
||||||
#[inline(always)]
|
let error_msg = LitStr::new(
|
||||||
async fn #raw_name
|
&format!("an error occurred whilest deserializing {}", param_name),
|
||||||
}.to_tokens(tokens);
|
Span::call_site(),
|
||||||
|
);
|
||||||
Paren::default().surround(tokens, |tokens|{
|
let return_from_deser_error = if self.has_returns {
|
||||||
quote!{ &self, data: ::std::vec::Vec<u8> }.to_tokens(tokens);
|
quote! {
|
||||||
});
|
return Err(::rnex_core::rmc::response::ErrorCode::Core_InvalidArgument);
|
||||||
|
}
|
||||||
if self.has_returns {
|
} else {
|
||||||
|
quote! {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let attribs = fold_tokenable(attribs.iter());
|
||||||
quote! {
|
quote! {
|
||||||
-> ::core::result::Result<Vec<u8>, ErrorCode>
|
#attribs
|
||||||
}.to_tokens(tokens);
|
let Ok(#param_name) =
|
||||||
|
<#param_type as rnex_core::rmc::structures::RmcSerialize>::deserialize(
|
||||||
|
&mut cursor
|
||||||
|
) else{
|
||||||
|
log::error!(#error_msg);
|
||||||
|
#return_from_deser_error
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
let call_params = fold_tokenable(parameters.iter().map(|(param_name, _, attribs)| {
|
||||||
|
let attribs = fold_tokenable(attribs.iter());
|
||||||
|
quote! {
|
||||||
|
#attribs
|
||||||
|
#param_name,
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
let optional_method_return = if *has_returns {
|
||||||
|
quote! {
|
||||||
|
let retval = retval?;
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
rnex_core::rmc::structures::RmcSerialize::serialize(&retval, &mut vec).ok();
|
||||||
|
Ok(vec)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {}
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[inline(always)]
|
||||||
|
#attribs
|
||||||
|
async fn #raw_name (&self, data: ::std::vec::Vec<u8>) #optional_return{
|
||||||
|
let mut cursor = ::std::io::Cursor::new(data);
|
||||||
|
#deser_params
|
||||||
|
let retval = self.#name(#call_params).await;
|
||||||
|
#optional_method_return
|
||||||
}
|
}
|
||||||
|
|
||||||
Brace::default().surround(tokens, |tokens|{
|
|
||||||
quote! { let mut cursor = ::std::io::Cursor::new(data); }.to_tokens(tokens);
|
|
||||||
|
|
||||||
for (param_name, param_type) in parameters{
|
|
||||||
quote!{
|
|
||||||
let Ok(#param_name) =
|
|
||||||
<#param_type as rnex_core::rmc::structures::RmcSerialize>::deserialize(
|
|
||||||
&mut cursor
|
|
||||||
) else
|
|
||||||
}.to_tokens(tokens);
|
|
||||||
|
|
||||||
let error_msg = LitStr::new(&format!("an error occurred whilest deserializing {}", param_name), Span::call_site());
|
|
||||||
|
|
||||||
if self.has_returns {
|
|
||||||
quote! {
|
|
||||||
{
|
|
||||||
log::error!(#error_msg);
|
|
||||||
return Err(rnex_core::rmc::response::ErrorCode::Core_InvalidArgument);
|
|
||||||
};
|
|
||||||
}.to_tokens(tokens)
|
|
||||||
} else {
|
|
||||||
quote! {
|
|
||||||
{
|
|
||||||
log::error!(#error_msg);
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
}.to_tokens(tokens)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
quote!{
|
|
||||||
let retval = self.#name
|
|
||||||
}.to_tokens(tokens);
|
|
||||||
|
|
||||||
Paren::default().surround(tokens, |tokens|{
|
|
||||||
for (paren_name, _) in parameters{
|
|
||||||
quote!{#paren_name,}.to_tokens(tokens);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
quote!{
|
|
||||||
.await;
|
|
||||||
}.to_tokens(tokens);
|
|
||||||
|
|
||||||
if *has_returns{
|
|
||||||
quote!{
|
|
||||||
let retval = retval?;
|
|
||||||
let mut vec = Vec::new();
|
|
||||||
rnex_core::rmc::structures::RmcSerialize::serialize(&retval, &mut vec).ok();
|
|
||||||
Ok(vec)
|
|
||||||
}.to_tokens(tokens);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
quote!{
|
let generate_rmc_call_proto = || {
|
||||||
|
let method_entries = fold_tokenable(methods.iter().map(|m| {
|
||||||
|
let ProtoMethodData {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
attributes,
|
||||||
|
..
|
||||||
|
} = m;
|
||||||
|
|
||||||
|
let attribs = fold_tokenable(attributes.iter());
|
||||||
|
let raw_name = Ident::new(&format!("raw_{}", name), name.span());
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#attribs
|
||||||
|
#id => self.#raw_name(data).await,
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
let optional_notimpl_return = if self.has_returns {
|
||||||
|
quote! {
|
||||||
|
Err(rnex_core::rmc::response::ErrorCode::Core_NotImplemented)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {}
|
||||||
|
};
|
||||||
|
|
||||||
|
let optional_result_sendback = if *has_returns {
|
||||||
|
quote! {
|
||||||
|
rnex_core::rmc::response::send_result(
|
||||||
|
remote_response_connection,
|
||||||
|
ret,
|
||||||
|
#id,
|
||||||
|
method_id,
|
||||||
|
call_id,
|
||||||
|
).await
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {}
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
async fn rmc_call_proto(
|
async fn rmc_call_proto(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -132,71 +282,38 @@ impl RmcProtocolData {
|
||||||
method_id: u32,
|
method_id: u32,
|
||||||
call_id: u32,
|
call_id: u32,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
)
|
){
|
||||||
}.to_tokens(tokens);
|
let ret = match method_id{
|
||||||
|
#method_entries
|
||||||
Brace::default().surround(tokens, |tokens|{
|
v => {
|
||||||
quote! {
|
|
||||||
let ret = match method_id
|
|
||||||
}.to_tokens(tokens);
|
|
||||||
|
|
||||||
Brace::default().surround(tokens, |tokens|{
|
|
||||||
for method in methods{
|
|
||||||
let ProtoMethodData{
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
..
|
|
||||||
} = method;
|
|
||||||
|
|
||||||
let raw_name = Ident::new(&format!("raw_{}", name), name.span());
|
|
||||||
|
|
||||||
|
|
||||||
quote!{
|
|
||||||
#id => self.#raw_name(data).await,
|
|
||||||
}.to_tokens(tokens);
|
|
||||||
}
|
|
||||||
quote!{
|
|
||||||
v =>
|
|
||||||
}.to_tokens(tokens);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Brace::default().surround(tokens, |tokens|{
|
|
||||||
quote!{
|
|
||||||
log::error!("(protocol {})unimplemented method id called on protocol: {}", #id, v);
|
log::error!("(protocol {})unimplemented method id called on protocol: {}", #id, v);
|
||||||
}.to_tokens(tokens);
|
#optional_notimpl_return
|
||||||
if self.has_returns {
|
|
||||||
quote! {
|
|
||||||
Err(rnex_core::rmc::response::ErrorCode::Core_NotImplemented)
|
|
||||||
}.to_tokens(tokens);
|
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
#optional_result_sendback
|
||||||
});
|
|
||||||
|
|
||||||
Semi::default().to_tokens(tokens);
|
|
||||||
|
|
||||||
if *has_returns{
|
|
||||||
quote!{
|
|
||||||
rnex_core::rmc::response::send_result(
|
|
||||||
remote_response_connection,
|
|
||||||
ret,
|
|
||||||
#id,
|
|
||||||
method_id,
|
|
||||||
call_id,
|
|
||||||
).await
|
|
||||||
}.to_tokens(tokens);
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// this gives us the name which the identifier of the corresponding Raw trait
|
||||||
|
let raw_name = Ident::new(&format!("Raw{}", name), name.span());
|
||||||
|
let proto_raw_methods = fold_tokenable(self.methods.iter().map(|m| generate_raw_method(m)));
|
||||||
|
let rmc_call_proto = generate_rmc_call_proto();
|
||||||
|
|
||||||
|
// boilerplate tokens which all raw traits need
|
||||||
quote! {
|
quote! {
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[allow(unused_must_use)]
|
||||||
|
pub trait #raw_name: #name{
|
||||||
|
#proto_raw_methods
|
||||||
|
#rmc_call_proto
|
||||||
|
}
|
||||||
impl<T: #name> #raw_name for T{}
|
impl<T: #name> #raw_name for T{}
|
||||||
}
|
}
|
||||||
.to_tokens(tokens);
|
.to_token_stream()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_raw_remote_trait(&self, tokens: &mut TokenStream) {
|
fn generate_raw_remote_trait(&self) -> TokenStream {
|
||||||
let Self {
|
let Self {
|
||||||
has_returns,
|
has_returns,
|
||||||
name,
|
name,
|
||||||
|
|
@ -207,101 +324,92 @@ impl RmcProtocolData {
|
||||||
|
|
||||||
// this gives us the name which the identifier of the corresponding Raw trait
|
// this gives us the name which the identifier of the corresponding Raw trait
|
||||||
let remote_name = Ident::new(&format!("Remote{}", name), name.span());
|
let remote_name = Ident::new(&format!("Remote{}", name), name.span());
|
||||||
|
let generate_remote_method = |m: &ProtoMethodData| -> TokenStream {
|
||||||
|
let ProtoMethodData {
|
||||||
|
name,
|
||||||
|
parameters,
|
||||||
|
ret_val,
|
||||||
|
attributes,
|
||||||
|
id: method_id,
|
||||||
|
} = m;
|
||||||
|
|
||||||
|
let params = fold_tokenable(parameters.iter().map(|(ident, ty, attr)| {
|
||||||
|
let attrs = fold_tokenable(attr.iter());
|
||||||
|
quote! { #attrs #ident: #ty, }
|
||||||
|
}));
|
||||||
|
|
||||||
|
let optional_questionmark_operator = if self.has_returns {
|
||||||
|
quote! {
|
||||||
|
?
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {}
|
||||||
|
};
|
||||||
|
|
||||||
|
let param_serialize = fold_tokenable(parameters.iter().map(|(name, ty, attrs)|{
|
||||||
|
let attrs = fold_tokenable(attrs.iter());
|
||||||
|
quote!{
|
||||||
|
#attrs
|
||||||
|
rnex_core::result::ResultExtension::display_err_or_some(
|
||||||
|
<#ty as rnex_core::rmc::structures::RmcSerialize>::serialize(
|
||||||
|
&#name,
|
||||||
|
&mut cursor
|
||||||
|
)
|
||||||
|
).ok_or(rnex_core::rmc::response::ErrorCode::Core_InvalidArgument)#optional_questionmark_operator ;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
let make_call = if *has_returns {
|
||||||
|
quote! {
|
||||||
|
rnex_core::result::ResultExtension::display_err_or_some(
|
||||||
|
rmc_conn.make_raw_call(&message).await
|
||||||
|
).ok_or(rnex_core::rmc::response::ErrorCode::Core_Exception)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
rnex_core::result::ResultExtension::display_err_or_some(
|
||||||
|
rmc_conn.make_raw_call_no_response(&message).await
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let attribs = fold_tokenable(attributes.iter());
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#attribs
|
||||||
|
async fn #name(&self, #params) #ret_val{
|
||||||
|
let mut send_data = ::std::vec::Vec::new();
|
||||||
|
let mut cursor = ::std::io::Cursor::new(&mut send_data);
|
||||||
|
#param_serialize
|
||||||
|
|
||||||
|
let call_id = rand::random();
|
||||||
|
|
||||||
|
let message = rnex_core::rmc::message::RMCMessage{
|
||||||
|
call_id,
|
||||||
|
method_id: #method_id,
|
||||||
|
protocol_id: #proto_id,
|
||||||
|
rest_of_data: send_data
|
||||||
|
};
|
||||||
|
|
||||||
|
let rmc_conn = <Self as rnex_core::rmc::protocols::HasRmcConnection>::get_connection(self);
|
||||||
|
|
||||||
|
#make_call
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let remote_methods = fold_tokenable(methods.iter().map(|m| generate_remote_method(m)));
|
||||||
|
|
||||||
// boilerplate tokens which all raw traits need
|
|
||||||
quote! {
|
quote! {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[allow(unused_must_use)]
|
#[allow(unused_must_use)]
|
||||||
pub trait #remote_name: rnex_core::rmc::protocols::HasRmcConnection
|
pub trait #remote_name: rnex_core::rmc::protocols::HasRmcConnection{
|
||||||
}
|
#remote_methods
|
||||||
.to_tokens(tokens);
|
|
||||||
|
|
||||||
// generate the body of the raw protocol trait
|
|
||||||
Brace::default().surround(tokens, |tokens|{
|
|
||||||
//generate each raw method
|
|
||||||
for method in methods{
|
|
||||||
let ProtoMethodData {
|
|
||||||
name,
|
|
||||||
parameters,
|
|
||||||
ret_val,
|
|
||||||
id: method_id,
|
|
||||||
..
|
|
||||||
} = method;
|
|
||||||
|
|
||||||
quote!{
|
|
||||||
async fn #name
|
|
||||||
}.to_tokens(tokens);
|
|
||||||
|
|
||||||
Paren::default().surround(tokens, |tokens|{
|
|
||||||
quote!{ &self, }.to_tokens(tokens);
|
|
||||||
for (param_ident, param_type) in parameters{
|
|
||||||
quote!{ #param_ident: #param_type, }.to_tokens(tokens);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
quote!{
|
|
||||||
#ret_val
|
|
||||||
}.to_tokens(tokens);
|
|
||||||
|
|
||||||
Brace::default().surround(tokens, |tokens|{
|
|
||||||
quote! {
|
|
||||||
let mut send_data = Vec::new();
|
|
||||||
let mut cursor = ::std::io::Cursor::new(&mut send_data);
|
|
||||||
}.to_tokens(tokens);
|
|
||||||
|
|
||||||
for (param_name, param_type) in parameters{
|
|
||||||
quote!{
|
|
||||||
rnex_core::result::ResultExtension::display_err_or_some(
|
|
||||||
<#param_type as rnex_core::rmc::structures::RmcSerialize>::serialize(
|
|
||||||
&#param_name,
|
|
||||||
&mut cursor
|
|
||||||
)
|
|
||||||
).ok_or(rnex_core::rmc::response::ErrorCode::Core_InvalidArgument)
|
|
||||||
}.to_tokens(tokens);
|
|
||||||
if self.has_returns {
|
|
||||||
quote! {
|
|
||||||
?;
|
|
||||||
}.to_tokens(tokens)
|
|
||||||
} else {
|
|
||||||
quote! {
|
|
||||||
;
|
|
||||||
}.to_tokens(tokens)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
quote!{
|
|
||||||
let call_id = rand::random();
|
|
||||||
|
|
||||||
let message = rnex_core::rmc::message::RMCMessage{
|
|
||||||
call_id,
|
|
||||||
method_id: #method_id,
|
|
||||||
protocol_id: #proto_id,
|
|
||||||
rest_of_data: send_data
|
|
||||||
};
|
|
||||||
|
|
||||||
let rmc_conn = <Self as rnex_core::rmc::protocols::HasRmcConnection>::get_connection(self);
|
|
||||||
}.to_tokens(tokens);
|
|
||||||
|
|
||||||
if *has_returns{
|
|
||||||
quote!{
|
|
||||||
rnex_core::result::ResultExtension::display_err_or_some(
|
|
||||||
rmc_conn.make_raw_call(&message).await
|
|
||||||
).ok_or(rnex_core::rmc::response::ErrorCode::Core_Exception)
|
|
||||||
}.to_tokens(tokens);
|
|
||||||
} else {
|
|
||||||
quote!{
|
|
||||||
rnex_core::result::ResultExtension::display_err_or_some(
|
|
||||||
rmc_conn.make_raw_call_no_response(&message).await
|
|
||||||
);
|
|
||||||
}.to_tokens(tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_raw_info(&self, tokens: &mut TokenStream) {
|
fn generate_raw_info(&self) -> TokenStream {
|
||||||
let Self { name, id, .. } = self;
|
let Self { name, id, .. } = self;
|
||||||
|
|
||||||
let raw_info_name = Ident::new(&format!("Raw{}Info", name), Span::call_site());
|
let raw_info_name = Ident::new(&format!("Raw{}Info", name), Span::call_site());
|
||||||
|
|
@ -314,14 +422,13 @@ impl RmcProtocolData {
|
||||||
pub const PROTOCOL_ID: u16 = #id;
|
pub const PROTOCOL_ID: u16 = #id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.to_tokens(tokens);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToTokens for RmcProtocolData {
|
impl ToTokens for RmcProtocolData {
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
self.generate_raw_trait(tokens);
|
self.generate_raw_trait().to_tokens(tokens);
|
||||||
self.generate_raw_info(tokens);
|
self.generate_raw_info().to_tokens(tokens);
|
||||||
self.generate_raw_remote_trait(tokens);
|
self.generate_raw_remote_trait().to_tokens(tokens);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
426
macros/src/rmc_struct.rs
Normal file
426
macros/src/rmc_struct.rs
Normal file
|
|
@ -0,0 +1,426 @@
|
||||||
|
use proc_macro2::{Literal, Span, TokenStream};
|
||||||
|
use quote::quote;
|
||||||
|
use syn::{
|
||||||
|
bracketed, parse::Parse, punctuated::Punctuated, token::Bracket, DataEnum, DataStruct,
|
||||||
|
DeriveInput, Field, Fields, Ident, Meta, Token, Variant,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::util::fold_tokenable;
|
||||||
|
|
||||||
|
struct RmcStructAttrVersion {
|
||||||
|
bracket: Bracket,
|
||||||
|
delim: Token![,],
|
||||||
|
feature_name: Literal,
|
||||||
|
struct_version: Literal,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RmcStructAttr {
|
||||||
|
base_ver: Literal,
|
||||||
|
versions: Option<(Token![,], Punctuated<RmcStructAttrVersion, Token![,]>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for RmcStructAttr {
|
||||||
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
|
let base_ver = input.parse()?;
|
||||||
|
|
||||||
|
if let Some(seperator) = input.parse()? {
|
||||||
|
let mut punctuated = Punctuated::new();
|
||||||
|
loop {
|
||||||
|
punctuated.push_value(input.parse()?);
|
||||||
|
if let Some(punct) = input.parse()? {
|
||||||
|
punctuated.push_punct(punct);
|
||||||
|
} else {
|
||||||
|
return Ok(Self {
|
||||||
|
base_ver,
|
||||||
|
versions: Some((seperator, punctuated)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(Self {
|
||||||
|
base_ver,
|
||||||
|
versions: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RmcStructAttr {
|
||||||
|
fn versions(&self) -> impl Iterator<Item = &RmcStructAttrVersion> {
|
||||||
|
self.versions.iter().flat_map(|v| v.1.iter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for RmcStructAttrVersion {
|
||||||
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
|
let content;
|
||||||
|
let bracket = bracketed!(content in input);
|
||||||
|
let (feature_name, delim, struct_version) =
|
||||||
|
content.call(|s| Ok((s.parse()?, s.parse()?, s.parse()?)))?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
bracket,
|
||||||
|
delim,
|
||||||
|
feature_name,
|
||||||
|
struct_version,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_write_size_struct(
|
||||||
|
s: &DataStruct,
|
||||||
|
with_potential_header: bool,
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
// this is fine and works because of a quirk where the sizes of the structs dont change
|
||||||
|
// if we ignore wether or not a struct extends the other struct or has it as a field
|
||||||
|
|
||||||
|
let base_size = fold_tokenable(s.fields.iter().map(|f| {
|
||||||
|
let ident = f.ident.as_ref().unwrap();
|
||||||
|
let attrs = fold_tokenable(f.attrs.iter().filter(|a| {
|
||||||
|
if let Some(i) = a.meta.path().get_ident() {
|
||||||
|
i.to_string() != "extends"
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
quote! {
|
||||||
|
#attrs
|
||||||
|
sum += rnex_core::rmc::structures::RmcSerialize::serialize_write_size(&self.#ident)?;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
let optional_struct_header_calc = if with_potential_header {
|
||||||
|
quote! { sum += (if rnex_core::config::FEATURE_HAS_STRUCT_HEADER{ 5 } else { 0 }); }
|
||||||
|
} else {
|
||||||
|
quote! {}
|
||||||
|
};
|
||||||
|
quote! {
|
||||||
|
let mut sum = 0;
|
||||||
|
#base_size
|
||||||
|
#optional_struct_header_calc
|
||||||
|
Ok(sum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn generate_serialize_struct(
|
||||||
|
extended_struct: Option<&Field>,
|
||||||
|
elems: &[&Field],
|
||||||
|
with_header: bool,
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
fn gen_elem_serialize(f: &Field) -> TokenStream {
|
||||||
|
let ident = f.ident.as_ref().unwrap();
|
||||||
|
let attrs = fold_tokenable(f.attrs.iter().filter(|a| {
|
||||||
|
if let Some(i) = a.meta.path().get_ident() {
|
||||||
|
i.to_string() != "extends"
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
quote! {
|
||||||
|
#attrs
|
||||||
|
rnex_core::rmc::structures::RmcSerialize::serialize(&self.#ident, writer)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let optional_extended_struct = if let Some(f) = extended_struct {
|
||||||
|
gen_elem_serialize(f)
|
||||||
|
} else {
|
||||||
|
quote! {}
|
||||||
|
};
|
||||||
|
let elems = fold_tokenable(elems.iter().map(|e| gen_elem_serialize(e)));
|
||||||
|
let ser_body = if with_header {
|
||||||
|
quote! {
|
||||||
|
rnex_core::rmc::structures::rmc_struct::write_struct(
|
||||||
|
writer,
|
||||||
|
Self::version().unwrap(),
|
||||||
|
rnex_core::rmc::structures::helpers::len_of_write(
|
||||||
|
|writer|{
|
||||||
|
#elems
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|writer|{
|
||||||
|
#elems
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
elems
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#optional_extended_struct
|
||||||
|
#ser_body
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn generate_deserialize_struct(
|
||||||
|
s: &DataStruct,
|
||||||
|
extended_struct: Option<&Field>,
|
||||||
|
elems: &[&Field],
|
||||||
|
with_header: bool,
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
fn gen_elem_serialize(f: &Field) -> TokenStream {
|
||||||
|
let ident = f.ident.as_ref().unwrap();
|
||||||
|
let ty = &f.ty;
|
||||||
|
let attrs = fold_tokenable(f.attrs.iter().filter(|a| {
|
||||||
|
if let Some(i) = a.meta.path().get_ident() {
|
||||||
|
i.to_string() != "extends"
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
quote! {
|
||||||
|
#attrs
|
||||||
|
let #ident: #ty = rnex_core::rmc::structures::RmcSerialize::deserialize(reader)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let optional_extended_struct = if let Some(f) = extended_struct {
|
||||||
|
gen_elem_serialize(f)
|
||||||
|
} else {
|
||||||
|
quote! {}
|
||||||
|
};
|
||||||
|
let elems = fold_tokenable(elems.iter().map(|e| gen_elem_serialize(e)));
|
||||||
|
let struct_ctor_content = fold_tokenable(s.fields.iter().map(|f| {
|
||||||
|
let ident = f.ident.as_ref().unwrap();
|
||||||
|
let attrs = fold_tokenable(f.attrs.iter().filter(|a| {
|
||||||
|
if let Some(i) = a.meta.path().get_ident() {
|
||||||
|
i.to_string() != "extends"
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
quote! { #attrs #ident, }
|
||||||
|
}));
|
||||||
|
let de_body_inner = quote! {
|
||||||
|
#elems
|
||||||
|
Ok(Self{
|
||||||
|
#struct_ctor_content
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let de_body = if with_header {
|
||||||
|
quote! {
|
||||||
|
Ok(rnex_core::rmc::structures::rmc_struct::read_struct(reader, Self::version().unwrap(), move |mut reader|{
|
||||||
|
#de_body_inner
|
||||||
|
})?)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
de_body_inner
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#optional_extended_struct
|
||||||
|
#de_body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_struct_version(attr: Option<&RmcStructAttr>) -> proc_macro2::TokenStream {
|
||||||
|
if let Some(attr) = attr {
|
||||||
|
let base_ver = &attr.base_ver;
|
||||||
|
let if_else_chain = fold_tokenable(attr.versions().map(|v| {
|
||||||
|
let version_val = &v.struct_version;
|
||||||
|
let feature = &v.feature_name;
|
||||||
|
quote! {
|
||||||
|
if cfg!(feature = #feature){
|
||||||
|
#version_val
|
||||||
|
} else
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
Some(#if_else_chain {
|
||||||
|
#base_ver
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! { None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rmc_serialize_struct(
|
||||||
|
s: &DataStruct,
|
||||||
|
derive_input: &DeriveInput,
|
||||||
|
) -> (
|
||||||
|
proc_macro2::TokenStream,
|
||||||
|
proc_macro2::TokenStream,
|
||||||
|
Option<proc_macro2::TokenStream>,
|
||||||
|
Option<proc_macro2::TokenStream>,
|
||||||
|
) {
|
||||||
|
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")
|
||||||
|
&& matches!(a.meta, Meta::List(_))
|
||||||
|
});
|
||||||
|
|
||||||
|
let struct_attr: Option<RmcStructAttr> = struct_attr.map(|a| a.parse_args().unwrap());
|
||||||
|
let struct_attr = struct_attr.as_ref();
|
||||||
|
|
||||||
|
let extended_struct = 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")
|
||||||
|
})
|
||||||
|
});
|
||||||
|
let elements: Vec<_> = s
|
||||||
|
.fields
|
||||||
|
.iter()
|
||||||
|
.filter(|f| {
|
||||||
|
!f.attrs.iter().any(|a| {
|
||||||
|
a.path().segments.len() == 1
|
||||||
|
&& a.path()
|
||||||
|
.segments
|
||||||
|
.first()
|
||||||
|
.is_some_and(|p| p.ident.to_string() == "extends")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let elements = &elements[..];
|
||||||
|
|
||||||
|
let serialize = generate_serialize_struct(extended_struct, elements, struct_attr.is_some());
|
||||||
|
let deserialize =
|
||||||
|
generate_deserialize_struct(s, extended_struct, elements, struct_attr.is_some());
|
||||||
|
let write_size = generate_write_size_struct(s, struct_attr.is_some());
|
||||||
|
let version = generate_struct_version(struct_attr);
|
||||||
|
|
||||||
|
(serialize, deserialize, Some(write_size), Some(version))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn field_to_ident(field: &Field, idx: usize) -> Ident {
|
||||||
|
if let Some(i) = &field.ident {
|
||||||
|
i.clone()
|
||||||
|
} else {
|
||||||
|
Ident::new(&format!("field_{}", idx), Span::call_site())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn variant_to_pattern_and_fields(variant: &Variant) -> (proc_macro2::TokenStream, Vec<Field>) {
|
||||||
|
match &variant.fields {
|
||||||
|
Fields::Named(n) => {
|
||||||
|
let inner = n
|
||||||
|
.named
|
||||||
|
.iter()
|
||||||
|
.map(|f| {
|
||||||
|
let attrs = fold_tokenable(f.attrs.iter());
|
||||||
|
let ident = f.ident.as_ref().unwrap();
|
||||||
|
|
||||||
|
quote! { #attrs #ident }
|
||||||
|
})
|
||||||
|
.reduce(|a, b| quote! {#a, #b});
|
||||||
|
|
||||||
|
(quote! {{#inner}}, n.named.iter().cloned().collect())
|
||||||
|
}
|
||||||
|
Fields::Unnamed(n) => {
|
||||||
|
let inner = n
|
||||||
|
.unnamed
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, f)| {
|
||||||
|
let attrs = fold_tokenable(f.attrs.iter());
|
||||||
|
let name = field_to_ident(f, i);
|
||||||
|
|
||||||
|
quote! { #attrs #name }
|
||||||
|
})
|
||||||
|
.reduce(|a, b| quote! {#a, #b});
|
||||||
|
|
||||||
|
(quote! {(#inner)}, n.unnamed.iter().cloned().collect())
|
||||||
|
}
|
||||||
|
Fields::Unit => (quote! {}, vec![]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rmc_generate_serialize_enum(
|
||||||
|
enum_data: &DataEnum,
|
||||||
|
repr_ty: &Ident,
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
let match_content = fold_tokenable(enum_data.variants.iter().map(|v|{
|
||||||
|
let ident = &v.ident;
|
||||||
|
let descriminant = &v.discriminant.as_ref().expect("every variant must have a descriminant to be a valid rmc struct").1;
|
||||||
|
let (pattern, fields) = variant_to_pattern_and_fields(v);
|
||||||
|
let inner = fold_tokenable(fields.iter().enumerate().map(|(i, f)|{
|
||||||
|
let ty = &f.ty;
|
||||||
|
let name = field_to_ident(&f, i);
|
||||||
|
quote! {<#ty as rnex_core::rmc::structures::RmcSerialize>::serialize(#name, writer)?;}
|
||||||
|
}));
|
||||||
|
quote!{
|
||||||
|
Self::#ident #pattern => {
|
||||||
|
<#repr_ty as rnex_core::rmc::structures::RmcSerialize>::serialize(&#descriminant, writer)?;
|
||||||
|
#inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
quote! {
|
||||||
|
match self{
|
||||||
|
#match_content
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn rmc_generate_deserialize_enum(
|
||||||
|
enum_data: &DataEnum,
|
||||||
|
repr_ty: &Ident,
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
let match_content = fold_tokenable(enum_data.variants.iter().map(|v| {
|
||||||
|
let ident = &v.ident;
|
||||||
|
let descriminant = &v
|
||||||
|
.discriminant
|
||||||
|
.as_ref()
|
||||||
|
.expect("every variant must have a descriminant to be a valid rmc struct")
|
||||||
|
.1;
|
||||||
|
let (pattern, fields) = variant_to_pattern_and_fields(v);
|
||||||
|
let inner = fold_tokenable(fields.iter().enumerate().map(|(i, f)| {
|
||||||
|
let ty = &f.ty;
|
||||||
|
let name = field_to_ident(&f, i);
|
||||||
|
quote! {let #name = <#ty as rnex_core::rmc::structures::RmcSerialize>::deserialize(reader)?;}
|
||||||
|
}));
|
||||||
|
quote! {
|
||||||
|
#descriminant => {
|
||||||
|
#inner
|
||||||
|
|
||||||
|
Self::#ident #pattern
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
let discriminant = <#repr_ty as rnex_core::rmc::structures::RmcSerialize>::deserialize(reader)?;
|
||||||
|
|
||||||
|
Ok(match discriminant{
|
||||||
|
#match_content
|
||||||
|
v => {
|
||||||
|
return Err(rnex_core::rmc::structures::Error::UnexpectedValue(v as u64))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rmc_serialize_enum(
|
||||||
|
enum_data: &DataEnum,
|
||||||
|
derive_input: &DeriveInput,
|
||||||
|
) -> (
|
||||||
|
proc_macro2::TokenStream,
|
||||||
|
proc_macro2::TokenStream,
|
||||||
|
Option<proc_macro2::TokenStream>,
|
||||||
|
Option<proc_macro2::TokenStream>,
|
||||||
|
) {
|
||||||
|
let repr_attr = derive_input.attrs.iter().find(|a| {
|
||||||
|
a.path().segments.len() == 1
|
||||||
|
&& a.path()
|
||||||
|
.segments
|
||||||
|
.first()
|
||||||
|
.is_some_and(|p| p.ident.to_string() == "repr")
|
||||||
|
});
|
||||||
|
let Some(repr_attr) = repr_attr else {
|
||||||
|
panic!("missing repr attribute");
|
||||||
|
};
|
||||||
|
|
||||||
|
let ty: Ident = repr_attr.parse_args().unwrap();
|
||||||
|
|
||||||
|
let serialize = rmc_generate_serialize_enum(&enum_data, &ty);
|
||||||
|
let deserialize = rmc_generate_deserialize_enum(&enum_data, &ty);
|
||||||
|
|
||||||
|
(serialize, deserialize, None, None)
|
||||||
|
}
|
||||||
10
macros/src/util.rs
Normal file
10
macros/src/util.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::ToTokens;
|
||||||
|
|
||||||
|
// todo: return a wrapper struct implementing ToTokens over the iterator instead as to avoid unnescesary allocations with the token stream
|
||||||
|
pub fn fold_tokenable<T: ToTokens>(list: impl Iterator<Item = T>) -> TokenStream {
|
||||||
|
list.fold(TokenStream::new(), |mut s, i| {
|
||||||
|
i.to_tokens(&mut s);
|
||||||
|
s
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -210,6 +210,7 @@ pub async fn new_backend_connection(
|
||||||
mod test {
|
mod test {
|
||||||
use crate::{VIRTUAL_PORT_INSECURE, VIRTUAL_PORT_SECURE};
|
use crate::{VIRTUAL_PORT_INSECURE, VIRTUAL_PORT_SECURE};
|
||||||
|
|
||||||
|
#[test]
|
||||||
fn test_virtual_port_correct() {
|
fn test_virtual_port_correct() {
|
||||||
println!("{:?}", VIRTUAL_PORT_INSECURE);
|
println!("{:?}", VIRTUAL_PORT_INSECURE);
|
||||||
println!("{:?}", VIRTUAL_PORT_SECURE);
|
println!("{:?}", VIRTUAL_PORT_SECURE);
|
||||||
|
|
|
||||||
|
|
@ -447,7 +447,7 @@ mod test {
|
||||||
|
|
||||||
let bytes = &bytes[0x6..];
|
let bytes = &bytes[0x6..];
|
||||||
|
|
||||||
let header_data: [u8; 8] = bytes.try_into().unwrap();
|
let _: [u8; 8] = bytes.try_into().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,7 @@ use cfg_if::cfg_if;
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use macros::rmc_struct;
|
use macros::rmc_struct;
|
||||||
use rnex_core::prudp::socket_addr::PRUDPSockAddr;
|
use rnex_core::prudp::socket_addr::PRUDPSockAddr;
|
||||||
use rnex_core::rmc::protocols::notifications::{
|
use rnex_core::rmc::protocols::notifications::{NotificationEvent, RemoteNotification};
|
||||||
self, Notification, NotificationEvent, RemoteNotification,
|
|
||||||
};
|
|
||||||
use rnex_core::rmc::protocols::ranking::{
|
use rnex_core::rmc::protocols::ranking::{
|
||||||
CompetitionRankingGetParam, CompetitionRankingScoreData, CompetitionRankingScoreInfo,
|
CompetitionRankingGetParam, CompetitionRankingScoreData, CompetitionRankingScoreInfo,
|
||||||
};
|
};
|
||||||
|
|
@ -53,7 +51,6 @@ 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::Error;
|
|
||||||
use crate::rmc::structures::matchmake::MatchmakeSessionSearchCriteria;
|
use crate::rmc::structures::matchmake::MatchmakeSessionSearchCriteria;
|
||||||
|
|
||||||
cfg_if! {
|
cfg_if! {
|
||||||
|
|
@ -185,15 +182,13 @@ impl MatchmakeExtension for User {
|
||||||
Ok(Vec::new())
|
Ok(Vec::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "v3-5-0")]
|
||||||
async fn update_progress_score(&self, gid: u32, progress: u8) -> Result<(), ErrorCode> {
|
async fn update_progress_score(&self, gid: u32, progress: u8) -> Result<(), ErrorCode> {
|
||||||
#[cfg(feature = "v3-5-0")]
|
let session = self.matchmake_manager.get_session(gid).await?;
|
||||||
{
|
|
||||||
let session = self.matchmake_manager.get_session(gid).await?;
|
|
||||||
|
|
||||||
let mut session = session.lock().await;
|
let mut session = session.lock().await;
|
||||||
|
|
||||||
session.session.progress_score = progress;
|
session.session.progress_score = progress;
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -442,7 +437,7 @@ impl MatchmakeExtension for User {
|
||||||
|
|
||||||
async fn get_friend_notification_data(
|
async fn get_friend_notification_data(
|
||||||
&self,
|
&self,
|
||||||
ty: i32,
|
_ty: i32,
|
||||||
) -> Result<Vec<NotificationEvent>, ErrorCode> {
|
) -> Result<Vec<NotificationEvent>, ErrorCode> {
|
||||||
Ok(vec![])
|
Ok(vec![])
|
||||||
}
|
}
|
||||||
|
|
@ -503,7 +498,7 @@ impl MatchmakeExtension for User {
|
||||||
&self,
|
&self,
|
||||||
gid: u32,
|
gid: u32,
|
||||||
message: String,
|
message: String,
|
||||||
dont_care_block_list: bool,
|
_dont_care_block_list: bool,
|
||||||
//participation_count: u16,
|
//participation_count: u16,
|
||||||
) -> Result<Vec<u8>, ErrorCode> {
|
) -> Result<Vec<u8>, ErrorCode> {
|
||||||
let sess = self.matchmake_manager.get_session(gid).await?;
|
let sess = self.matchmake_manager.get_session(gid).await?;
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ pub trait MatchmakeExtension {
|
||||||
async fn get_playing_session(&self, pids: Vec<u32>) -> Result<Vec<()>, ErrorCode>;
|
async fn get_playing_session(&self, pids: Vec<u32>) -> Result<Vec<()>, ErrorCode>;
|
||||||
|
|
||||||
#[method_id(34)]
|
#[method_id(34)]
|
||||||
|
#[cfg(feature = "v3-5-0")]
|
||||||
async fn update_progress_score(&self, gid: u32, progress: u8) -> Result<(), ErrorCode>;
|
async fn update_progress_score(&self, gid: u32, progress: u8) -> Result<(), ErrorCode>;
|
||||||
#[method_id(38)]
|
#[method_id(38)]
|
||||||
async fn create_matchmake_session_with_param(
|
async fn create_matchmake_session_with_param(
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ use crate::rmc::response::{ErrorCode, RMCResponse, RMCResponseResult};
|
||||||
use crate::rmc::structures;
|
use crate::rmc::structures;
|
||||||
use crate::rmc::structures::RmcSerialize;
|
use crate::rmc::structures::RmcSerialize;
|
||||||
use crate::util::{SendingBufferConnection, SplittableBufferConnection};
|
use crate::util::{SendingBufferConnection, SplittableBufferConnection};
|
||||||
use futures::FutureExt;
|
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,6 @@
|
||||||
use macros::{RmcSerialize, method_id, rmc_proto};
|
use macros::{method_id, rmc_proto};
|
||||||
|
|
||||||
use rnex_core::{
|
use rnex_core::rmc::response::ErrorCode;
|
||||||
PID,
|
|
||||||
rmc::{response::ErrorCode, structures::any::Any},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{kerberos::KerberosDateTime, rmc::protocols::friends::NNAInfo};
|
|
||||||
|
|
||||||
#[rmc_proto(110)]
|
#[rmc_proto(110)]
|
||||||
pub trait Utility {
|
pub trait Utility {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ pub enum Error {
|
||||||
StationUrlInvalid,
|
StationUrlInvalid,
|
||||||
#[error("error formatting text: {0}")]
|
#[error("error formatting text: {0}")]
|
||||||
FormatError(#[from] fmt::Error),
|
FormatError(#[from] fmt::Error),
|
||||||
|
#[error("uncategorized rmc error occurred: {0}")]
|
||||||
|
Other(Box<dyn std::error::Error + Send + Sync>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
@ -36,10 +38,11 @@ pub mod primitives;
|
||||||
pub mod qbuffer;
|
pub mod qbuffer;
|
||||||
pub mod qresult;
|
pub mod qresult;
|
||||||
pub mod ranking;
|
pub mod ranking;
|
||||||
|
pub mod resultsrange;
|
||||||
pub mod rmc_struct;
|
pub mod rmc_struct;
|
||||||
pub mod string;
|
pub mod string;
|
||||||
|
pub mod string_set;
|
||||||
pub mod variant;
|
pub mod variant;
|
||||||
pub mod resultsrange;
|
|
||||||
|
|
||||||
pub trait RmcSerialize {
|
pub trait RmcSerialize {
|
||||||
fn serialize(&self, writer: &mut impl Write) -> Result<()>;
|
fn serialize(&self, writer: &mut impl Write) -> Result<()>;
|
||||||
|
|
@ -67,6 +70,9 @@ pub trait RmcSerialize {
|
||||||
fn name() -> &'static str {
|
fn name() -> &'static str {
|
||||||
"NoNameSpecified"
|
"NoNameSpecified"
|
||||||
}
|
}
|
||||||
|
fn version() -> Option<u8> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RmcSerialize for () {
|
impl RmcSerialize for () {
|
||||||
|
|
|
||||||
86
rnex-core/src/rmc/structures/string_set.rs
Normal file
86
rnex-core/src/rmc/structures/string_set.rs
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
use std::{collections::HashSet, hash::Hash, str::FromStr, string::ToString};
|
||||||
|
|
||||||
|
use rnex_core::rmc::structures::RmcSerialize;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct StringSet<T: FromStr + ToString + Eq>(HashSet<T>)
|
||||||
|
where
|
||||||
|
<T as FromStr>::Err: std::error::Error + Send + Sync + 'static;
|
||||||
|
|
||||||
|
impl<T: FromStr + ToString + Eq + Hash> PartialEq for StringSet<T>
|
||||||
|
where
|
||||||
|
<T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0.iter().eq(&other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: FromStr + ToString + Eq + Hash> ToString for StringSet<T>
|
||||||
|
where
|
||||||
|
<T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.reduce(|a, b| format!("{}|{}", a, b))
|
||||||
|
.unwrap_or(String::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: FromStr + ToString + Eq + Hash> FromStr for StringSet<T>
|
||||||
|
where
|
||||||
|
<T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
type Err = Box<dyn std::error::Error + Send + Sync>;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(Self(s.split("|").map(T::from_str).try_fold(
|
||||||
|
HashSet::new(),
|
||||||
|
|mut a, b| -> Result<HashSet<T>, Self::Err> {
|
||||||
|
a.insert(b.map_err(Box::new)?);
|
||||||
|
Ok(a)
|
||||||
|
},
|
||||||
|
)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: FromStr + ToString + Eq + Hash> RmcSerialize for StringSet<T>
|
||||||
|
where
|
||||||
|
<T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
fn deserialize(reader: &mut impl std::io::prelude::Read) -> super::Result<Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
Self::from_str(&String::deserialize(reader)?).map_err(super::Error::Other)
|
||||||
|
}
|
||||||
|
fn serialize(&self, writer: &mut impl std::io::prelude::Write) -> super::Result<()> {
|
||||||
|
self.to_string().serialize(writer)
|
||||||
|
}
|
||||||
|
fn serialize_write_size(&self) -> super::Result<u32> {
|
||||||
|
self.to_string().serialize_write_size()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use crate::rmc::structures::string_set::StringSet;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let str_val = "0|100|200|10|110|210|20|120|220|30|130|230";
|
||||||
|
let set: StringSet<u32> = StringSet::from_str(str_val).unwrap();
|
||||||
|
let string_2 = set.to_string();
|
||||||
|
let reset: StringSet<u32> = StringSet::from_str(&string_2).unwrap();
|
||||||
|
|
||||||
|
for val in &set.0 {
|
||||||
|
if !reset.0.contains(&val) {
|
||||||
|
panic!("sets arent equivalent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,7 +12,7 @@ pub struct ConnectionInitData {
|
||||||
mod test {
|
mod test {
|
||||||
use std::{
|
use std::{
|
||||||
io::Cursor,
|
io::Cursor,
|
||||||
net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4},
|
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
||||||
13
test-all.sh
Executable file
13
test-all.sh
Executable file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
EDITIONS=$(yq ea "." editions.yaml | yq 'keys[]')
|
||||||
|
IFS=$'\n'
|
||||||
|
while IFS=$'\n' read -r EDITION; do
|
||||||
|
if [[ $(yq ea ".$EDITION.include-in-checkall" editions.yaml) == "true" ]]
|
||||||
|
then
|
||||||
|
export EDITION
|
||||||
|
./test-edition.sh $EDITION
|
||||||
|
fi
|
||||||
|
done <<< "$EDITIONS"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue