feat: almost done with rework communication with remotes now works
This commit is contained in:
parent
fa37331780
commit
a7c36c39ef
14 changed files with 696 additions and 155 deletions
54
Cargo.lock
generated
54
Cargo.lock
generated
|
|
@ -614,17 +614,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.0-rc.0"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a78f88e84d239c7f2619ae8b091603c26208e1cb322571f5a29d6806f56ee5e"
|
||||
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"rustix",
|
||||
"wasi 0.13.3+wasi-0.2.2",
|
||||
"wasm-bindgen",
|
||||
"windows-targets 0.52.6",
|
||||
"r-efi",
|
||||
"wasi 0.14.2+wasi-0.2.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1051,6 +1048,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rand 0.9.0",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
|
|
@ -1425,6 +1423,12 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
|
|
@ -1438,12 +1442,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.0-beta.3"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fccbfebb3972a41a31c605a59207d9fba5489b9a87d9d87024cb6df73a32ec7"
|
||||
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0-beta.1",
|
||||
"rand_core 0.9.0-beta.1",
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.3",
|
||||
"zerocopy 0.8.14",
|
||||
]
|
||||
|
||||
|
|
@ -1459,12 +1463,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0-beta.1"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f16da77124f4ee9fabd55ce6540866e9101431863b4876de58b68797f331adf2"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.9.0-beta.1",
|
||||
"rand_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1478,12 +1482,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.0-beta.1"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a98fa0b8309344136abe6244130311e76997e546f76fae8054422a7539b43df7"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
dependencies = [
|
||||
"getrandom 0.3.0-rc.0",
|
||||
"zerocopy 0.8.14",
|
||||
"getrandom 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1871,7 +1874,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"paste",
|
||||
"prost",
|
||||
"rand 0.9.0-beta.3",
|
||||
"rand 0.9.0",
|
||||
"rc4",
|
||||
"rocket",
|
||||
"rustls",
|
||||
|
|
@ -1882,6 +1885,7 @@ dependencies = [
|
|||
"tokio-stream",
|
||||
"tonic",
|
||||
"tonic-build",
|
||||
"typenum",
|
||||
"v_byte_macros",
|
||||
]
|
||||
|
||||
|
|
@ -2268,9 +2272,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
|||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||
|
||||
[[package]]
|
||||
name = "ubyte"
|
||||
|
|
@ -2348,9 +2352,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
|||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.13.3+wasi-0.2.2"
|
||||
version = "0.14.2+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
|
||||
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
|
@ -2624,9 +2628,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.33.0"
|
||||
version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ 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"
|
||||
typenum = "1.18.0"
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build = "0.12.3"
|
||||
|
|
|
|||
|
|
@ -13,4 +13,5 @@ proc-macro = true
|
|||
quote = "1.0.38"
|
||||
proc-macro2 = "1.0.93"
|
||||
syn = { version = "2.0.98", features = ["full"] }
|
||||
rand = "0.9.0"
|
||||
|
||||
|
|
|
|||
|
|
@ -323,7 +323,8 @@ pub fn rmc_proto(attr: TokenStream, input: TokenStream) -> TokenStream{
|
|||
ProtoMethodData{
|
||||
id,
|
||||
name: func.sig.ident.clone(),
|
||||
parameters: funcs
|
||||
parameters: funcs,
|
||||
ret_val: func.sig.output.clone()
|
||||
}
|
||||
}).collect()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
use proc_macro2::{Ident, Span, TokenStream, TokenTree};
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::{LitInt, Token, Type};
|
||||
use syn::{LitInt, ReturnType, Token, Type};
|
||||
use syn::token::{Brace, Paren, Semi};
|
||||
|
||||
pub struct ProtoMethodData{
|
||||
pub id: LitInt,
|
||||
pub name: Ident,
|
||||
pub parameters: Vec<(Ident, Type)>
|
||||
pub parameters: Vec<(Ident, Type)>,
|
||||
pub ret_val: ReturnType,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -22,8 +23,8 @@ pub struct RmcProtocolData{
|
|||
pub methods: Vec<ProtoMethodData>
|
||||
}
|
||||
|
||||
impl ToTokens for RmcProtocolData{
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
impl RmcProtocolData{
|
||||
fn generate_raw_trait(&self, tokens: &mut TokenStream){
|
||||
let Self{
|
||||
has_returns,
|
||||
name,
|
||||
|
|
@ -73,7 +74,7 @@ impl ToTokens for RmcProtocolData{
|
|||
<#param_type as crate::rmc::structures::RmcSerialize>::deserialize(
|
||||
&mut cursor
|
||||
) else {
|
||||
return Err(ErrorCode::Core_InvalidArgument);
|
||||
return Err(crate::rmc::response::ErrorCode::Core_InvalidArgument);
|
||||
};
|
||||
}.to_tokens(tokens)
|
||||
}
|
||||
|
|
@ -156,6 +157,111 @@ impl ToTokens for RmcProtocolData{
|
|||
quote!{
|
||||
impl<T: #name> RawAuth for T{}
|
||||
}.to_tokens(tokens);
|
||||
}
|
||||
|
||||
fn generate_raw_remote_trait(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
has_returns,
|
||||
name,
|
||||
id: proto_id,
|
||||
methods,
|
||||
..
|
||||
} = self;
|
||||
|
||||
// this gives us the name which the identifier of the corresponding Raw trait
|
||||
let remote_name = Ident::new(&format!("Remote{}", name), name.span());
|
||||
|
||||
|
||||
// boilerplate tokens which all raw traits need
|
||||
quote!{
|
||||
#[doc(hidden)]
|
||||
pub trait #remote_name: crate::rmc::protocols::HasRmcConnection
|
||||
}.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!{
|
||||
crate::result::ResultExtension::display_err_or_some(
|
||||
<#param_type as crate::rmc::structures::RmcSerialize>::serialize(
|
||||
&#param_name,
|
||||
&mut cursor
|
||||
)
|
||||
).ok_or(crate::rmc::response::ErrorCode::Core_InvalidArgument)?;
|
||||
}.to_tokens(tokens)
|
||||
}
|
||||
|
||||
quote!{
|
||||
let call_id = rand::random();
|
||||
|
||||
let message = crate::rmc::message::RMCMessage{
|
||||
call_id,
|
||||
method_id: #method_id,
|
||||
protocol_id: #proto_id,
|
||||
rest_of_data: send_data
|
||||
};
|
||||
|
||||
let rmc_conn = <Self as crate::rmc::protocols::HasRmcConnection>::get_connection(self);
|
||||
}.to_tokens(tokens);
|
||||
|
||||
if *has_returns{
|
||||
quote!{
|
||||
crate::result::ResultExtension::display_err_or_some(
|
||||
rmc_conn.make_raw_call(&message).await
|
||||
).ok_or(crate::rmc::response::ErrorCode::Core_Exception)
|
||||
}.to_tokens(tokens);
|
||||
} else {
|
||||
quote!{
|
||||
crate::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){
|
||||
let Self{
|
||||
name,
|
||||
id,
|
||||
..
|
||||
} = self;
|
||||
|
||||
let raw_info_name = Ident::new(&format!("Raw{}Info", name), Span::call_site());
|
||||
|
||||
|
|
@ -171,3 +277,15 @@ impl ToTokens for RmcProtocolData{
|
|||
}
|
||||
}
|
||||
|
||||
impl ToTokens for RmcProtocolData{
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
self.generate_raw_trait(tokens);
|
||||
self.generate_raw_info(tokens);
|
||||
self.generate_raw_remote_trait(tokens);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
162
src/main.rs
162
src/main.rs
|
|
@ -7,73 +7,96 @@
|
|||
//! also the first and only current usage of rnex, expect this and rnex to be split into seperate
|
||||
//! repos soon.
|
||||
|
||||
use std::{env, fs};
|
||||
use std::fs::File;
|
||||
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||
use crate::rmc::protocols::auth::RemoteAuth;
|
||||
use crate::rmc::protocols::auth::RawAuthInfo;
|
||||
use crate::rmc::protocols::auth::RawAuth;
|
||||
use crate::nex::account::Account;
|
||||
use crate::prudp::packet::VirtualPort;
|
||||
use crate::prudp::router::Router;
|
||||
use crate::prudp::sockaddr::PRUDPSockAddr;
|
||||
use crate::prudp::socket::Unsecure;
|
||||
use chrono::{Local, SecondsFormat};
|
||||
use log::info;
|
||||
use once_cell::sync::Lazy;
|
||||
use simplelog::{ColorChoice, CombinedLogger, Config, LevelFilter, TerminalMode, TermLogger, WriteLogger};
|
||||
use crate::nex::account::Account;
|
||||
use crate::prudp::socket::Unsecure;
|
||||
use crate::prudp::packet::{VirtualPort};
|
||||
use crate::prudp::router::Router;
|
||||
use crate::prudp::sockaddr::PRUDPSockAddr;
|
||||
use simplelog::{
|
||||
ColorChoice, CombinedLogger, Config, LevelFilter, TermLogger, TerminalMode, WriteLogger,
|
||||
};
|
||||
use std::fs::File;
|
||||
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||
use std::{env, fs};
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{BitAnd, BitOr};
|
||||
use std::str::FromStr;
|
||||
use macros::rmc_struct;
|
||||
use crate::rmc::protocols::auth::Auth;
|
||||
use crate::rmc::protocols::{new_rmc_gateway_connection, OnlyRemote};
|
||||
use crate::rmc::response::ErrorCode;
|
||||
use crate::rmc::structures::any::Any;
|
||||
use crate::rmc::structures::connection_data::ConnectionData;
|
||||
use crate::rmc::structures::qresult::QResult;
|
||||
|
||||
mod endianness;
|
||||
mod prudp;
|
||||
pub mod rmc;
|
||||
//mod protocols;
|
||||
|
||||
mod nex;
|
||||
mod grpc;
|
||||
mod kerberos;
|
||||
mod nex;
|
||||
mod web;
|
||||
mod versions;
|
||||
mod result;
|
||||
|
||||
static KERBEROS_SERVER_PASSWORD: Lazy<String> = Lazy::new(||{
|
||||
static KERBEROS_SERVER_PASSWORD: Lazy<String> = Lazy::new(|| {
|
||||
env::var("AUTH_SERVER_PASSWORD")
|
||||
.ok()
|
||||
.unwrap_or("password".to_owned())
|
||||
});
|
||||
|
||||
static AUTH_SERVER_ACCOUNT: Lazy<Account> =
|
||||
Lazy::new(|| Account::new(1, "Quazal Authentication", &KERBEROS_SERVER_PASSWORD));
|
||||
static SECURE_SERVER_ACCOUNT: Lazy<Account> =
|
||||
Lazy::new(|| Account::new(2, "Quazal Rendez-Vous", &KERBEROS_SERVER_PASSWORD));
|
||||
|
||||
static AUTH_SERVER_ACCOUNT: Lazy<Account> = Lazy::new(|| Account::new(1, "Quazal Authentication", &KERBEROS_SERVER_PASSWORD));
|
||||
static SECURE_SERVER_ACCOUNT: Lazy<Account> = Lazy::new(|| Account::new(2, "Quazal Rendez-Vous", &KERBEROS_SERVER_PASSWORD));
|
||||
|
||||
static AUTH_SERVER_PORT: Lazy<u16> = Lazy::new(||{
|
||||
static AUTH_SERVER_PORT: Lazy<u16> = Lazy::new(|| {
|
||||
env::var("AUTH_SERVER_PORT")
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(10000)
|
||||
});
|
||||
static SECURE_SERVER_PORT: Lazy<u16> = Lazy::new(||{
|
||||
static SECURE_SERVER_PORT: Lazy<u16> = Lazy::new(|| {
|
||||
env::var("SECURE_SERVER_PORT")
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(10001)
|
||||
.unwrap_or(10002)
|
||||
});
|
||||
|
||||
static OWN_IP_PRIVATE: Lazy<Ipv4Addr> = Lazy::new(||{
|
||||
static OWN_IP_PRIVATE: Lazy<Ipv4Addr> = Lazy::new(|| {
|
||||
env::var("SERVER_IP")
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.expect("no public ip specified")
|
||||
});
|
||||
|
||||
static OWN_IP_PUBLIC: Lazy<String> = Lazy::new(||{
|
||||
env::var("SERVER_IP_PUBLIC")
|
||||
.unwrap_or(OWN_IP_PRIVATE.to_string())
|
||||
});
|
||||
static OWN_IP_PUBLIC: Lazy<String> =
|
||||
Lazy::new(|| env::var("SERVER_IP_PUBLIC").unwrap_or(OWN_IP_PRIVATE.to_string()));
|
||||
|
||||
static SECURE_STATION_URL: Lazy<String> = Lazy::new(||
|
||||
format!("prudps:/PID=2;sid=1;stream=10;type=2;address={};port={};CID=1", *OWN_IP_PUBLIC, *SECURE_SERVER_PORT)
|
||||
);
|
||||
static SECURE_STATION_URL: Lazy<String> = Lazy::new(|| {
|
||||
format!(
|
||||
"prudps:/PID=2;sid=1;stream=10;type=2;address={};port={};CID=1",
|
||||
*OWN_IP_PUBLIC, *SECURE_SERVER_PORT
|
||||
)
|
||||
});
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
CombinedLogger::init(
|
||||
vec![
|
||||
TermLogger::new(LevelFilter::Info, Config::default(), TerminalMode::Mixed, ColorChoice::Auto),
|
||||
CombinedLogger::init(vec![
|
||||
TermLogger::new(
|
||||
LevelFilter::Info,
|
||||
Config::default(),
|
||||
TerminalMode::Mixed,
|
||||
ColorChoice::Auto,
|
||||
),
|
||||
WriteLogger::new(LevelFilter::max(), Config::default(), {
|
||||
fs::create_dir_all("log").unwrap();
|
||||
let date = Local::now().to_rfc3339_opts(SecondsFormat::Secs, false);
|
||||
|
|
@ -85,9 +108,9 @@ async fn main() {
|
|||
} else {
|
||||
File::create(format!("log/{}", filename)).unwrap()
|
||||
}
|
||||
})
|
||||
]
|
||||
).unwrap();
|
||||
}),
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
dotenv::dotenv().ok();
|
||||
|
||||
|
|
@ -234,68 +257,63 @@ async fn start_secure_server() -> SecureServer{
|
|||
socket,
|
||||
}
|
||||
}*/
|
||||
/*
|
||||
|
||||
define_rmc_proto!(
|
||||
proto AuthClientProtocol{
|
||||
Auth
|
||||
}
|
||||
);*/
|
||||
);
|
||||
|
||||
impl Auth for AuthClient{
|
||||
async fn login(&self, name: String) -> Result<(), ErrorCode> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
//#[rmc_struct(AuthClientProtocol)]
|
||||
struct AuthClient{
|
||||
async fn login_ex(&self, name: String, extra_data: Any) -> Result<(QResult, u32, Vec<u8>, ConnectionData, String), ErrorCode> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn request_ticket(&self, source_pid: u32, destination_pid: u32) -> Result<(QResult, Vec<u8>), ErrorCode> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_pid(&self, username: String) -> Result<u32, ErrorCode> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_name(&self, pid: u32) -> Result<String, ErrorCode> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
async fn start_servers(){
|
||||
#[rmc_struct(AuthClientProtocol)]
|
||||
struct AuthClient {}
|
||||
|
||||
async fn start_servers() {
|
||||
|
||||
|
||||
//let auth_ip = SocketAddrV4::from_str("157.90.13.221:30039").unwrap();
|
||||
let auth_ip = SocketAddrV4::from_str("31.220.75.208:10000").unwrap();
|
||||
let auth_port = VirtualPort::new(1, 10);
|
||||
|
||||
let auth_sockaddr = PRUDPSockAddr::new(auth_ip, auth_port);
|
||||
|
||||
let (router_secure, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *SECURE_SERVER_PORT))
|
||||
.await
|
||||
.expect("unable to start router");
|
||||
|
||||
let a = tokio::spawn(async{
|
||||
let (router_auth, _) =
|
||||
Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *AUTH_SERVER_PORT)).await.expect("unable to start router");
|
||||
|
||||
let mut socket_auth = router_auth.add_socket(VirtualPort::new(1,10), Unsecure("CD&ML")).await
|
||||
.expect("unable to add socket");
|
||||
|
||||
let mut conn = socket_auth.accept().await.unwrap();
|
||||
info!("got conn");
|
||||
|
||||
if let Some(data) = conn.recv().await{
|
||||
let str = String::from_utf8(data).unwrap();
|
||||
|
||||
println!("{}", str)
|
||||
}
|
||||
});
|
||||
|
||||
let b = tokio::spawn(async{
|
||||
let auth_ip = SocketAddrV4::new(*OWN_IP_PRIVATE, *AUTH_SERVER_PORT);
|
||||
let auth_port = VirtualPort::new(1,10);
|
||||
|
||||
let auth_sockaddr = PRUDPSockAddr::new(auth_ip,auth_port);
|
||||
|
||||
|
||||
let (router_secure, _) =
|
||||
Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *SECURE_SERVER_PORT)).await.expect("unable to start router");
|
||||
|
||||
let mut socket_secure = router_secure.add_socket(VirtualPort::new(1,10), Unsecure("CD&ML")).await
|
||||
let mut socket_secure = router_secure
|
||||
.add_socket(VirtualPort::new(1, 10), Unsecure("CD&ML"))
|
||||
.await
|
||||
.expect("unable to add socket");
|
||||
|
||||
let conn = socket_secure.connect(auth_sockaddr).await.unwrap();
|
||||
|
||||
let conn = conn.duplicate_sender();
|
||||
let obj = new_rmc_gateway_connection(conn, OnlyRemote::<RemoteAuthClientProtocol>::new);
|
||||
|
||||
conn.send("Yippie".as_bytes().to_owned()).await;
|
||||
|
||||
info!("got conn");
|
||||
});
|
||||
|
||||
a.await;
|
||||
b.await;
|
||||
|
||||
/*
|
||||
/*
|
||||
#[cfg(feature = "auth")]
|
||||
let auth_server = start_auth_server().await;
|
||||
#[cfg(feature = "secure")]
|
||||
|
|
|
|||
23
src/result.rs
Normal file
23
src/result.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
use std::error::Error;
|
||||
use log::error;
|
||||
|
||||
pub trait ResultExtension{
|
||||
type Output;
|
||||
|
||||
fn display_err_or_some(self) -> Option<Self::Output>;
|
||||
}
|
||||
|
||||
impl<T, U: Error> ResultExtension for Result<T, U>{
|
||||
type Output = T;
|
||||
|
||||
fn display_err_or_some(self) -> Option<Self::Output> {
|
||||
match self{
|
||||
Ok(v) => Some(v),
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,9 +9,13 @@ use macros::{method_id, rmc_proto};
|
|||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||
#[rmc_proto(10)]
|
||||
pub trait Auth {
|
||||
/// representation of the `Login` method(for details see the
|
||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||
#[method_id(1)]
|
||||
async fn login(&self, name: String) -> Result<(), ErrorCode>;
|
||||
|
||||
/// representation of the `LoginEx` method(for details see the
|
||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||
#[method_id(2)]
|
||||
async fn login_ex(
|
||||
&self,
|
||||
|
|
@ -19,6 +23,8 @@ pub trait Auth {
|
|||
extra_data: Any,
|
||||
) -> Result<(QResult, u32, Vec<u8>, ConnectionData, String), ErrorCode>;
|
||||
|
||||
/// representation of the `RequestTicket` method(for details see the
|
||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||
#[method_id(3)]
|
||||
async fn request_ticket(
|
||||
&self,
|
||||
|
|
@ -26,9 +32,16 @@ pub trait Auth {
|
|||
destination_pid: u32,
|
||||
) -> Result<(QResult, Vec<u8>), ErrorCode>;
|
||||
|
||||
/// representation of the `GetPID` method(for details see the
|
||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||
#[method_id(4)]
|
||||
async fn get_pid(&self, username: String) -> Result<u32, ErrorCode>;
|
||||
|
||||
/// representation of the `LoginWithContext` method(for details see the
|
||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||
#[method_id(5)]
|
||||
async fn get_name(&self, pid: u32) -> Result<String, ErrorCode>;
|
||||
|
||||
// `LoginWithContext` is left out here because we don't need it right now and versioning still
|
||||
// needs to be figured out
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,52 +2,113 @@
|
|||
|
||||
pub mod auth;
|
||||
|
||||
use macros::method_id;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Add;
|
||||
use std::sync::{Arc, Condvar};
|
||||
use std::time::Duration;
|
||||
use crate::prudp::socket::{ExternalConnection, SendingConnection};
|
||||
use crate::rmc::message::RMCMessage;
|
||||
use crate::rmc::protocols::RemoteCallError::ConnectionBroke;
|
||||
use crate::rmc::response::{ErrorCode, RMCResponse, RMCResponseResult};
|
||||
use crate::rmc::structures;
|
||||
use crate::rmc::structures::connection_data::ConnectionData;
|
||||
use crate::rmc::structures::matchmake::AutoMatchmakeParam;
|
||||
use crate::rmc::structures::{Error, RmcSerialize};
|
||||
use async_trait::async_trait;
|
||||
use chrono::TimeDelta;
|
||||
use log::{error, info};
|
||||
use macros::method_id;
|
||||
use macros::{rmc_proto, rmc_struct};
|
||||
use paste::paste;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Cursor;
|
||||
use std::ops::{Add, Deref};
|
||||
use std::sync::{Arc, Condvar};
|
||||
use std::time::Duration;
|
||||
use thiserror::Error;
|
||||
use tokio::sync::{Mutex, Notify};
|
||||
use tokio::time::{sleep_until, Instant};
|
||||
use crate::prudp::socket::{ExternalConnection, SendingConnection};
|
||||
use crate::rmc::response::ErrorCode;
|
||||
use crate::rmc::structures::connection_data::ConnectionData;
|
||||
use crate::rmc::structures::Error;
|
||||
use crate::rmc::structures::matchmake::AutoMatchmakeParam;
|
||||
use crate::result::ResultExtension;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum RemoteCallError {
|
||||
#[error("Call to remote timed out whilest waiting on response.")]
|
||||
Timeout,
|
||||
#[error("A server side rmc error occurred: {0:?}")]
|
||||
ServerError(ErrorCode),
|
||||
#[error("Connection broke")]
|
||||
ConnectionBroke,
|
||||
#[error("Error reading response data")]
|
||||
InvalidResponse(#[from] structures::Error),
|
||||
}
|
||||
|
||||
pub struct RmcConnection(pub SendingConnection, pub RmcResponseReceiver);
|
||||
|
||||
pub struct RmcResponseReceiver(Notify, Mutex<HashMap<(u32), Vec<u8>>>);
|
||||
pub struct RmcResponseReceiver(Arc<Notify>, Arc<Mutex<HashMap<u32, RMCResponse>>>);
|
||||
|
||||
impl RmcResponseReceiver{
|
||||
impl RmcConnection {
|
||||
pub async fn make_raw_call<T: RmcSerialize>(
|
||||
&self,
|
||||
message: &RMCMessage,
|
||||
) -> Result<T, RemoteCallError> {
|
||||
self.make_raw_call_no_response(message).await?;
|
||||
|
||||
let data = self.1.get_response_data(message.call_id).await?;
|
||||
|
||||
let out = <T as RmcSerialize>::deserialize(&mut Cursor::new(data))?;
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
pub async fn make_raw_call_no_response(
|
||||
&self,
|
||||
message: &RMCMessage,
|
||||
) -> Result<(), RemoteCallError> {
|
||||
let message_data = message.to_data();
|
||||
|
||||
self.0.send(message_data).await.ok_or(ConnectionBroke)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcResponseReceiver {
|
||||
// returns none if timed out
|
||||
pub async fn get_response_data(&self, call_id: u32) -> Option<Vec<u8>>{
|
||||
pub async fn get_response_data(&self, call_id: u32) -> Result<Vec<u8>, RemoteCallError> {
|
||||
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);
|
||||
|
||||
let mut sleep_manual_unlock_fut = Instant::now();
|
||||
sleep_manual_unlock_fut += Duration::from_secs(4);
|
||||
|
||||
let sleep_manual_unlock_fut = sleep_until(sleep_manual_unlock_fut);
|
||||
tokio::pin!(sleep_manual_unlock_fut);
|
||||
|
||||
loop {
|
||||
let mut locked = self.1.lock().await;
|
||||
|
||||
if let Some(v) = locked.remove(&call_id){
|
||||
return Some(v);
|
||||
if let Some(v) = locked.remove(&call_id) {
|
||||
match v.response_result{
|
||||
RMCResponseResult::Success {
|
||||
data,
|
||||
..
|
||||
} => return Ok(data),
|
||||
RMCResponseResult::Error {
|
||||
error_code,
|
||||
..
|
||||
} => return Err(RemoteCallError::ServerError(error_code))
|
||||
}
|
||||
}
|
||||
|
||||
drop(locked);
|
||||
|
||||
|
||||
let notif_fut = self.0.notified();
|
||||
|
||||
|
||||
tokio::select! {
|
||||
_ = &mut sleep_manual_unlock_fut => {
|
||||
continue;
|
||||
}
|
||||
_ = &mut sleep_fut => {
|
||||
return None;
|
||||
return Err(RemoteCallError::Timeout);
|
||||
}
|
||||
_ = notif_fut => {
|
||||
continue;
|
||||
|
|
@ -57,28 +118,30 @@ impl RmcResponseReceiver{
|
|||
}
|
||||
}
|
||||
|
||||
pub trait HasRmcConnection{
|
||||
fn get_response_receiver(&self) -> &RmcConnection;
|
||||
pub trait HasRmcConnection {
|
||||
fn get_connection(&self) -> &RmcConnection;
|
||||
}
|
||||
|
||||
pub trait RemoteObject{
|
||||
pub trait RemoteObject {
|
||||
fn new(conn: RmcConnection) -> Self;
|
||||
}
|
||||
|
||||
impl RemoteObject for (){
|
||||
impl RemoteObject for () {
|
||||
fn new(_: RmcConnection) -> Self {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub trait RmcCallable{
|
||||
pub trait RmcCallable {
|
||||
//type Remote: RemoteObject;
|
||||
//fn new_callable(remote: Self::Remote);
|
||||
async fn rmc_call(&self, responder: &SendingConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec<u8>);
|
||||
async fn rmc_call(
|
||||
&self,
|
||||
responder: &SendingConnection,
|
||||
protocol_id: u16,
|
||||
method_id: u32,
|
||||
call_id: u32,
|
||||
rest: Vec<u8>,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! define_rmc_proto {
|
||||
(proto $name:ident{
|
||||
|
|
@ -98,7 +161,131 @@ macro_rules! define_rmc_proto {
|
|||
|
||||
struct [<Remote $name>](crate::rmc::protocols::RmcConnection);
|
||||
|
||||
impl crate::rmc::protocols::RemoteInstantiatable for [<Remote $name>]{
|
||||
fn new(conn: crate::rmc::protocols::RmcConnection) -> Self{
|
||||
Self(conn)
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::rmc::protocols::HasRmcConnection for [<Remote $name>]{
|
||||
fn get_connection(&self) -> &crate::rmc::protocols::RmcConnection{
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
impl [<Remote $protocol>] for [<Remote $name>]{}
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// This is a special case to allow unit to represent the fact that no object is represented.
|
||||
impl RmcCallable for () {
|
||||
async fn rmc_call(
|
||||
&self,
|
||||
remote_response_connection: &crate::prudp::socket::SendingConnection,
|
||||
protocol_id: u16,
|
||||
method_id: u32,
|
||||
call_id: u32,
|
||||
rest: Vec<u8>,
|
||||
) {
|
||||
//todo: maybe reply with not implemented(?)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RemoteInstantiatable{
|
||||
fn new(conn: RmcConnection) -> Self;
|
||||
}
|
||||
|
||||
pub struct OnlyRemote<T: RemoteInstantiatable>(T);
|
||||
|
||||
impl<T: RemoteInstantiatable> Deref for OnlyRemote<T>{
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RemoteInstantiatable> OnlyRemote<T>{
|
||||
pub fn new(conn: RmcConnection) -> Self{
|
||||
Self(T::new(conn))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RemoteInstantiatable> RmcCallable for OnlyRemote<T>{
|
||||
async fn rmc_call(&self, responder: &SendingConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec<u8>) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_incoming<T: RmcCallable>(
|
||||
mut connection: ExternalConnection,
|
||||
remote: Arc<T>,
|
||||
notify: Arc<Notify>,
|
||||
incoming: Arc<Mutex<HashMap<u32, RMCResponse>>>,
|
||||
) {
|
||||
let sending_conn = connection.duplicate_sender();
|
||||
|
||||
while let Some(v) = connection.recv().await{
|
||||
let Some(proto_id) = v.get(5) else {
|
||||
error!("received too small rmc message.");
|
||||
error!("ending rmc gateway.");
|
||||
return
|
||||
};
|
||||
|
||||
if proto_id & 0x80 == 0{
|
||||
let Some(response) = RMCResponse::new(&mut Cursor::new(v)).display_err_or_some() else {
|
||||
error!("ending rmc gateway.");
|
||||
return
|
||||
};
|
||||
|
||||
info!("got rmc response");
|
||||
|
||||
let mut locked = incoming.lock().await;
|
||||
|
||||
locked.insert(response.get_call_id(), response);
|
||||
notify.notify_waiters();
|
||||
} else {
|
||||
let Some(message) = RMCMessage::new(&mut Cursor::new(v)).display_err_or_some() else {
|
||||
error!("ending rmc gateway.");
|
||||
return
|
||||
};
|
||||
|
||||
info!("got rmc request");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_rmc_gateway_connection<T: RmcCallable + Sync + Send + 'static,F>(conn: ExternalConnection, create_internal: F) -> Arc<T>
|
||||
where
|
||||
F: FnOnce(RmcConnection) -> T,
|
||||
{
|
||||
let notify = Arc::new(Notify::new());
|
||||
let incoming: Arc<Mutex<HashMap<u32, RMCResponse>>> = Default::default();
|
||||
|
||||
let response_recv = RmcResponseReceiver(notify.clone(), incoming.clone());
|
||||
|
||||
let sending_conn = conn.duplicate_sender();
|
||||
|
||||
let rmc_conn = RmcConnection(sending_conn, response_recv);
|
||||
|
||||
let exposed_object = (create_internal)(rmc_conn);
|
||||
|
||||
let exposed_object = Arc::new(exposed_object);
|
||||
|
||||
{
|
||||
let exposed_object = exposed_object.clone();
|
||||
tokio::spawn(async move {
|
||||
handle_incoming(
|
||||
conn,
|
||||
exposed_object,
|
||||
notify,
|
||||
incoming
|
||||
).await;
|
||||
});
|
||||
}
|
||||
|
||||
exposed_object
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
use std::io;
|
||||
use std::io::{Write};
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::mem::transmute;
|
||||
use bytemuck::bytes_of;
|
||||
use log::error;
|
||||
use v_byte_macros::EnumTryInto;
|
||||
use crate::endianness::{ReadExtensions, IS_BIG_ENDIAN};
|
||||
use crate::prudp::packet::{PRUDPPacket};
|
||||
use crate::prudp::packet::flags::{NEED_ACK, RELIABLE};
|
||||
use crate::prudp::packet::PacketOption::FragmentId;
|
||||
use crate::prudp::packet::types::DATA;
|
||||
use crate::prudp::socket::{ExternalConnection, SendingConnection};
|
||||
use crate::rmc::response::ErrorCode::Core_Exception;
|
||||
use crate::rmc::structures::qresult::ERROR_MASK;
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
use crate::web::DirectionalData::{Incoming, Outgoing};
|
||||
|
|
@ -30,6 +34,69 @@ pub struct RMCResponse {
|
|||
}
|
||||
|
||||
impl RMCResponse {
|
||||
pub fn new(stream: &mut (impl Seek + Read)) -> io::Result<Self>{
|
||||
// ignore the size for now this will only be used for checking
|
||||
let _: u32 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
let protocol_id: u8 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
/*let protocol_id: u16 = match protocol_id{
|
||||
0x7F => {
|
||||
stream.read_struct(IS_BIG_ENDIAN)?
|
||||
},
|
||||
_ => protocol_id as u16
|
||||
};*/
|
||||
|
||||
let is_success: u8 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
let response_result = if is_success == 0x01{
|
||||
let call_id: u32 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
let method_id: u32 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
let method_id = method_id & (!0x8000);
|
||||
|
||||
let mut data: Vec<u8> = Vec::new();
|
||||
|
||||
stream.read_to_end(&mut data)?;
|
||||
|
||||
|
||||
RMCResponseResult::Success {
|
||||
call_id,
|
||||
method_id,
|
||||
data
|
||||
}
|
||||
} else {
|
||||
let error_code: u32 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
let error_code = error_code & (!0x80000000);
|
||||
let call_id: u32 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
RMCResponseResult::Error {
|
||||
error_code: {
|
||||
match ErrorCode::try_from(error_code){
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error!("invalid error code {:#010x}", error_code);
|
||||
Core_Exception
|
||||
}
|
||||
}
|
||||
},
|
||||
call_id,
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self{
|
||||
protocol_id,
|
||||
response_result
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_call_id(&self) -> u32{
|
||||
match &self.response_result{
|
||||
RMCResponseResult::Success { call_id, ..} => *call_id,
|
||||
RMCResponseResult::Error { call_id, .. } => *call_id
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_data(self) -> Vec<u8> {
|
||||
generate_response(self.protocol_id, self.response_result).expect("failed to generate response")
|
||||
}
|
||||
|
|
@ -120,6 +187,7 @@ pub async fn send_response(connection: &SendingConnection, rmcresponse: RMCRespo
|
|||
//taken from kinnays error list directly
|
||||
#[allow(nonstandard_style)]
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, EnumTryInto)]
|
||||
pub enum ErrorCode {
|
||||
Core_Unknown = 0x00010001,
|
||||
Core_NotImplemented = 0x00010002,
|
||||
|
|
|
|||
|
|
@ -2,15 +2,24 @@ use std::io::{Read, Write};
|
|||
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
use super::{Result, RmcSerialize};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Any{
|
||||
pub name: String,
|
||||
pub data: Vec<u8>
|
||||
}
|
||||
|
||||
impl RmcSerialize for Any{
|
||||
fn serialize(&self, _writer: &mut dyn Write) -> Result<()> {
|
||||
todo!()
|
||||
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
|
||||
self.name.serialize(writer)?;
|
||||
|
||||
let u32_len = self.data.len() as u32;
|
||||
|
||||
u32_len.serialize(writer)?;
|
||||
u32_len.serialize(writer)?;
|
||||
|
||||
self.data.serialize(writer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn deserialize(mut reader: &mut dyn Read) -> Result<Self> {
|
||||
let name = String::deserialize(reader)?;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use bytemuck::bytes_of;
|
|||
use crate::kerberos::KerberosDateTime;
|
||||
use crate::rmc::structures::{rmc_struct, RmcSerialize};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ConnectionData<'a>{
|
||||
pub station_url: &'a str,
|
||||
pub special_protocols: Vec<u8>,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use crate::rmc::structures::{RmcSerialize, Result};
|
|||
|
||||
pub const ERROR_MASK: u32 = 1 << 31;
|
||||
|
||||
#[derive(Pod, Zeroable, Copy, Clone, SwapEndian)]
|
||||
#[derive(Pod, Zeroable, Copy, Clone, SwapEndian, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct QResult(u32);
|
||||
|
||||
|
|
|
|||
97
src/versions.rs
Normal file
97
src/versions.rs
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
use std::marker::PhantomData;
|
||||
use std::ops::{BitAnd, BitOr};
|
||||
use typenum::{Cmp, IsEqual, IsLess, IsLessOrEqual, Unsigned, U1, U2, U3};
|
||||
|
||||
/// This trait represents a version at compile time
|
||||
trait Version{
|
||||
type Major: Unsigned;
|
||||
type Minor: Unsigned;
|
||||
}
|
||||
|
||||
/// This struct contains nothing and is used to represent specific versions as an instance of
|
||||
/// [`Version`]. It is instances as `Ver<Major, Minor>`
|
||||
struct Ver<MAJ: Unsigned, MIN: Unsigned>{
|
||||
_phantom: PhantomData<(MAJ, MIN)>
|
||||
}
|
||||
|
||||
impl<MAJ: Unsigned, MIN: Unsigned> Version for Ver<MAJ, MIN>{
|
||||
type Major = MAJ;
|
||||
type Minor = MIN;
|
||||
}
|
||||
|
||||
/// Represents two versions which can be compared
|
||||
trait ComparableVersion<T: Version>: Version{
|
||||
type IsAtLeast: SameOrUnit;
|
||||
}
|
||||
|
||||
impl<T: Version, U: Version> ComparableVersion<T> for U where
|
||||
<T as Version>::Major: Cmp<Self::Major>,
|
||||
<T as Version>::Minor: IsLessOrEqual<Self::Minor>,
|
||||
<T as Version>::Major: IsEqual<
|
||||
Self::Major,
|
||||
Output: BitAnd<
|
||||
typenum::LeEq<T::Minor, Self::Minor>
|
||||
>,
|
||||
>,
|
||||
<T as Version>::Major: IsLess<
|
||||
Self::Major,
|
||||
Output: BitOr<
|
||||
typenum::And<
|
||||
typenum::Eq<T::Major, Self::Major>,
|
||||
typenum::LeEq<T::Minor, Self::Minor>,
|
||||
>,
|
||||
Output: SameOrUnit
|
||||
>
|
||||
> {
|
||||
|
||||
type IsAtLeast = typenum::Or<
|
||||
typenum::Le<T::Major, Self::Major>,
|
||||
typenum::And<
|
||||
typenum::Eq<T::Major, Self::Major>,
|
||||
typenum::LeEq<T::Minor, Self::Minor>,
|
||||
>
|
||||
>;
|
||||
}
|
||||
|
||||
|
||||
/// Simple check for testing if the `TEST` version is at least `REQ` or higher.
|
||||
type VersionAbove<REQ, TEST> = <TEST as ComparableVersion<REQ>>::IsAtLeast;
|
||||
|
||||
trait VersionIsAtLeast<VER: Version>{}
|
||||
|
||||
impl<VER: Version, T: ComparableVersion<VER, IsAtLeast = typenum::True>> VersionIsAtLeast<VER> for T{}
|
||||
|
||||
|
||||
/// Trait for containing the result of elements which only conditionally exist
|
||||
trait CondElemResult{
|
||||
type Output;
|
||||
}
|
||||
|
||||
/// Empty helper struct which only servers to give a concrete type when creating fields in rmc
|
||||
/// structs which have a version requirement. This is not meant to be used directly, use
|
||||
/// [`MinVersion`] instead.
|
||||
struct MinVersionElementHelper<T, REQUIRED: Version, VER: Version + ComparableVersion<REQUIRED>>{
|
||||
_phantom: PhantomData<(T, REQUIRED, VER)>
|
||||
}
|
||||
|
||||
/// This should be used either with [`typenum::True`] or [`typenum::False`]. When `True` the [`Self::Output`]
|
||||
/// will be the same as the `T` you put into Output. When `False` it will always be `()`
|
||||
trait SameOrUnit{
|
||||
type Output<T>;
|
||||
}
|
||||
|
||||
impl SameOrUnit for typenum::True{
|
||||
type Output<T> = T;
|
||||
}
|
||||
|
||||
impl SameOrUnit for typenum::False{
|
||||
type Output<T> = ();
|
||||
}
|
||||
|
||||
impl<T, REQUIRED: Version, VER: Version + ComparableVersion<REQUIRED>> CondElemResult for MinVersionElementHelper<T, REQUIRED, VER> where {
|
||||
type Output = <<VER as ComparableVersion<REQUIRED>>::IsAtLeast as SameOrUnit>::Output<T>;
|
||||
}
|
||||
|
||||
/// When the version condition is met the field will exist and will simply be `T` if not it will be
|
||||
/// replaced by `()`. Use this when you need to add versioning to rmc structs.
|
||||
type MinVersion<T, REQUIRED, VER> = <MinVersionElementHelper<T, REQUIRED, VER> as CondElemResult>::Output;
|
||||
Loading…
Add table
Add a link
Reference in a new issue