Add server op dispatcher

This commit is contained in:
NGnius (Graham) 2025-02-09 15:05:39 -05:00
parent 488a57d5cb
commit 8cb4425c83
10 changed files with 328 additions and 0 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
/target
/*/target

100
server/Cargo.lock generated Normal file
View file

@ -0,0 +1,100 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "array-init"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc"
[[package]]
name = "binrw"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d4bca59c20d6f40c2cc0802afbe1e788b89096f61bdf7aeea6bf00f10c2909b"
dependencies = [
"array-init",
"binrw_derive",
"bytemuck",
]
[[package]]
name = "binrw_derive"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8ba42866ce5bced2645bfa15e97eef2c62d2bdb530510538de8dd3d04efff3c"
dependencies = [
"either",
"owo-colors",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "bytemuck"
version = "1.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
[[package]]
name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "owo-colors"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]]
name = "polariton"
version = "0.1.0"
dependencies = [
"binrw",
]
[[package]]
name = "polariton_server"
version = "0.1.0"
dependencies = [
"polariton",
]
[[package]]
name = "proc-macro2"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [
"proc-macro2",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"

7
server/Cargo.toml Normal file
View file

@ -0,0 +1,7 @@
[package]
name = "polariton_server"
version = "0.1.0"
edition = "2021"
[dependencies]
polariton = { version = "0.1.0", path = "../" }

0
server/src/events/mod.rs Normal file
View file

2
server/src/lib.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod events;
pub mod operations;

View file

@ -0,0 +1,24 @@
#[derive(Default)]
pub struct Ack<const CODE: u8, U: Send + Sync> {
_user_ty: std::marker::PhantomData<U>,
}
impl <const CODE: u8, U: Send + Sync> super::Operation for Ack<CODE, U> {
type State = ();
type User = U;
fn handle(&self, _: polariton::operation::ParameterTable, _: &mut Self::State, _: &Self::User) -> polariton::operation::OperationResponse {
polariton::operation::OperationResponse {
code: CODE,
return_code: 0,
message: polariton::operation::Typed::Null,
params: std::collections::HashMap::default().into(),
}
}
}
impl <const CODE: u8, U: Send + Sync> super::OperationCode for Ack<CODE, U> {
fn op_code() -> u8 {
CODE
}
}

View file

@ -0,0 +1,79 @@
use polariton::operation::{ParameterTable, OperationResponse};
pub struct Func<const CODE: u8, S: Send + Sync, U: Send + Sync, F: (Fn(ParameterTable, &mut S, &U) -> OperationResponse) + Send + Sync> {
_state_ty: std::marker::PhantomData<S>,
_user_ty: std::marker::PhantomData<U>,
func: F,
}
impl <const CODE: u8, S: Send + Sync, U: Send + Sync, F: (Fn(ParameterTable, &mut S, &U) -> OperationResponse) + Send + Sync> Func<CODE, S, U, F> {
pub fn new(f: F) -> Self {
Self {
_state_ty: std::marker::PhantomData::default(),
_user_ty: std::marker::PhantomData::default(),
func: f,
}
}
}
impl <const CODE: u8, S: Send + Sync, U: Send + Sync, F: (Fn(ParameterTable, &mut S, &U) -> OperationResponse) + Send + Sync> super::Operation for Func<CODE, S, U, F> {
type State = S;
type User = U;
fn handle(&self, p: polariton::operation::ParameterTable, s: &mut Self::State, u: &Self::User) -> OperationResponse {
(self.func)(p, s, u)
}
}
impl <const CODE: u8, S: Send + Sync, U: Send + Sync, F: (Fn(ParameterTable, &mut S, &U) -> OperationResponse) + Send + Sync> super::OperationCode for Func<CODE, S, U, F> {
fn op_code() -> u8 {
CODE
}
}
pub struct SimpleFunc<const CODE: u8, U: Send + Sync, F: (Fn(ParameterTable, &U) -> Result<ParameterTable, i16>) + Send + Sync> {
_user_ty: std::marker::PhantomData<U>,
func: F,
}
impl <const CODE: u8, U: Send + Sync, F: (Fn(ParameterTable, &U) -> Result<ParameterTable, i16>) + Send + Sync> SimpleFunc<CODE, U, F> {
pub fn new(f: F) -> Self {
Self {
_user_ty: std::marker::PhantomData::default(),
func: f,
}
}
}
impl <const CODE: u8, U: Send + Sync, F: (Fn(ParameterTable, &U) -> Result<ParameterTable, i16>) + Send + Sync> super::Operation for SimpleFunc<CODE, U, F> {
type State = ();
type User = U;
fn handle(&self, p: polariton::operation::ParameterTable, _: &mut Self::State, u: &Self::User) -> OperationResponse {
match (self.func)(p, u) {
Ok(p_out) => {
OperationResponse {
code: CODE,
return_code: 0,
message: polariton::operation::Typed::Null,
params: p_out,
}
},
Err(e_code) => {
OperationResponse {
code: CODE,
return_code: e_code,
message: polariton::operation::Typed::Null,
params: std::collections::HashMap::new().into(),
}
}
}
}
}
impl <const CODE: u8, U: Send + Sync, F: (Fn(ParameterTable, &U) -> Result<ParameterTable, i16>) + Send + Sync> super::OperationCode for SimpleFunc<CODE, U, F> {
fn op_code() -> u8 {
CODE
}
}

View file

@ -0,0 +1,92 @@
use std::collections::HashMap;
use std::sync::Mutex;
use polariton::operation::{OperationResponse, ParameterTable, OperationRequest};
use super::{Operation, OperationCode};
trait WrappedOperation<U: Send + Sync>: Send + Sync {
fn handle(&self, params: ParameterTable, user: &U) -> OperationResponse;
}
struct SyncedOpWrapper<S: Send + Sync, O: Operation<State=S>> {
state: Mutex<S>,
handler: O,
}
impl <S: Send + Sync, U: Send + Sync, O: Operation<State=S, User=U>> WrappedOperation<U> for SyncedOpWrapper<S, O> {
fn handle(&self, params: ParameterTable, user: &U) -> OperationResponse {
self.handler.handle(params, &mut *self.state.lock().unwrap(), user)
}
}
struct StatelessOpWrapper<O: Operation<State=()>> {
handler: O,
}
impl <U: Send + Sync, O: Operation<State=(), User=U>> WrappedOperation<U> for StatelessOpWrapper<O> {
fn handle(&self, params: ParameterTable, user: &U) -> OperationResponse {
self.handler.handle(params, &mut (), user)
}
}
pub struct OperationsHandler<U: Send + Sync> {
op_map: HashMap<u8, Box<dyn WrappedOperation<U>>>,
}
impl <U: Send + Sync> OperationsHandler<U> {
const SERVICE_MAPPING_KEY: u8 = 0;
pub fn new() -> Self {
Self {
op_map: Default::default(),
}
}
pub fn with_state<S: Send + Sync + 'static, O: Operation<State=S, User=U> + OperationCode + 'static>(mut self, op: O, state: S) -> Self {
self.op_map.insert(O::op_code(), Box::new(SyncedOpWrapper {
state: Mutex::new(state),
handler: op,
}));
self
}
pub fn without_state<O: Operation<State=(), User=U> + OperationCode + 'static>(mut self, op: O) -> Self {
self.op_map.insert(O::op_code(), Box::new(StatelessOpWrapper {
handler: op,
}));
self
}
fn try_pass_to_handler(&self, code: u8, params: ParameterTable, user:&U) -> OperationResponse {
if let Some(handler) = self.op_map.get(&code) {
handler.handle(params, user)
} else {
#[cfg(debug_assertions)]
panic!("No handler for op code {}, pls fix!! params: {:?}", code, params.clone().to_dict());
#[cfg(not(debug_assertions))]
{
log::debug!("No handler for op code {}, echoing params {:?}", code, params.clone().to_dict());
OperationResponse {
code,
return_code: 0,
message: polariton::operation::Typed::Null,
params,
}
}
}
}
pub fn handle_op(&self, user: &U, req: OperationRequest) -> OperationResponse {
let mut op_resp = self.try_pass_to_handler(req.code, req.params.clone(), user);
if let Some(svelto_service_id) = req.params.clone().to_dict().remove(&Self::SERVICE_MAPPING_KEY) {
// make sure to do GUID passthrough
let mut dict_params = op_resp.params.to_dict();
dict_params.insert(Self::SERVICE_MAPPING_KEY, svelto_service_id);
op_resp.params = dict_params.into();
}
op_resp
}
}

View file

@ -0,0 +1,11 @@
mod handler;
pub use handler::OperationsHandler;
mod traits;
pub use traits::{Operation, OperationCode};
mod ack;
pub use ack::Ack;
mod functional;
pub use functional::{Func, SimpleFunc};

View file

@ -0,0 +1,12 @@
use polariton::operation::{OperationResponse, ParameterTable};
pub trait Operation: Send + Sync {
type State;
type User;
fn handle(&self, params: ParameterTable, state: &mut Self::State, user: &Self::User) -> OperationResponse;
}
pub trait OperationCode {
fn op_code() -> u8;
}