diff --git a/Cargo.lock b/Cargo.lock index 8c02e63..eb1be40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1783,6 +1783,7 @@ dependencies = [ "futures-util", "stateroom", "tokio", + "tracing", ] [[package]] diff --git a/examples/binary-echo/src/lib.rs b/examples/binary-echo/src/lib.rs index 36eb397..baf68b0 100644 --- a/examples/binary-echo/src/lib.rs +++ b/examples/binary-echo/src/lib.rs @@ -1,13 +1,10 @@ use stateroom_wasm::prelude::*; #[stateroom_wasm] +#[derive(Default)] struct BinaryEcho; -impl SimpleStateroomService for BinaryEcho { - fn new(_: &str, _: &impl StateroomContext) -> Self { - BinaryEcho - } - +impl StateroomService for BinaryEcho { fn message(&mut self, _: ClientId, message: &str, ctx: &impl StateroomContext) { ctx.send_binary( MessageRecipient::Broadcast, diff --git a/examples/clock/src/lib.rs b/examples/clock/src/lib.rs index 1a2c6d9..c13db66 100644 --- a/examples/clock/src/lib.rs +++ b/examples/clock/src/lib.rs @@ -1,17 +1,17 @@ use stateroom_wasm::prelude::*; #[stateroom_wasm] -struct ClockServer(String, u32); +#[derive(Default)] +struct ClockServer(u32); -impl SimpleStateroomService for ClockServer { - fn new(room_id: &str, ctx: &impl StateroomContext) -> Self { +impl StateroomService for ClockServer { + fn init(&mut self, ctx: &impl StateroomContext) { ctx.set_timer(4000); - ClockServer(room_id.to_string(), 0) } fn timer(&mut self, ctx: &impl StateroomContext) { - ctx.send_message(MessageRecipient::Broadcast, &format!("Here in room {} from timer @ {}", self.0, self.1)); - self.1 += 1; + ctx.send_message(MessageRecipient::Broadcast, &format!("Timer @ {}", self.0)); + self.0 += 1; ctx.set_timer(4000); } } diff --git a/examples/counter-service/src/lib.rs b/examples/counter-service/src/lib.rs index ec539ce..c13684d 100644 --- a/examples/counter-service/src/lib.rs +++ b/examples/counter-service/src/lib.rs @@ -1,13 +1,10 @@ use stateroom_wasm::prelude::*; #[stateroom_wasm] +#[derive(Default)] struct SharedCounterServer(i32); -impl SimpleStateroomService for SharedCounterServer { - fn new(_: &str, _: &impl StateroomContext) -> Self { - SharedCounterServer(0) - } - +impl StateroomService for SharedCounterServer { fn message(&mut self, _: ClientId, message: &str, ctx: &impl StateroomContext) { match message { "increment" => self.0 += 1, diff --git a/examples/cpu-hog/src/lib.rs b/examples/cpu-hog/src/lib.rs index a2d7593..b8fbb3f 100644 --- a/examples/cpu-hog/src/lib.rs +++ b/examples/cpu-hog/src/lib.rs @@ -4,7 +4,8 @@ use stateroom_wasm::prelude::*; const SECONDS: u64 = 1_000_000_000; #[stateroom_wasm] -struct CpuHog(String); +#[derive(Default)] +struct CpuHog; fn get_time() -> u64 { unsafe { @@ -12,15 +13,11 @@ fn get_time() -> u64 { } } -impl SimpleStateroomService for CpuHog { - fn new(room_id: &str, _: &impl StateroomContext) -> Self { - CpuHog(room_id.to_string()) - } - +impl StateroomService for CpuHog { fn connect(&mut self, _: ClientId, ctx: &impl StateroomContext) { ctx.send_message( MessageRecipient::Broadcast, - &format!("Connected to room {}", self.0), + &format!("Connected."), ); let init_time = get_time(); @@ -33,7 +30,7 @@ impl SimpleStateroomService for CpuHog { ctx.send_message( MessageRecipient::Broadcast, - &format!("Finished in room {}", self.0), + &format!("Finished."), ); } } diff --git a/examples/echo-server/src/lib.rs b/examples/echo-server/src/lib.rs index f1cac83..c931595 100644 --- a/examples/echo-server/src/lib.rs +++ b/examples/echo-server/src/lib.rs @@ -1,13 +1,10 @@ use stateroom_wasm::prelude::*; #[stateroom_wasm] +#[derive(Default)] struct EchoServer; -impl SimpleStateroomService for EchoServer { - fn new(_: &str, _: &impl StateroomContext) -> Self { - EchoServer - } - +impl StateroomService for EchoServer { fn connect(&mut self, client_id: ClientId, ctx: &impl StateroomContext) { ctx.send_message(client_id, &format!("User {:?} connected.", client_id)); } diff --git a/examples/randomness/src/lib.rs b/examples/randomness/src/lib.rs index 30da1ad..55a625a 100644 --- a/examples/randomness/src/lib.rs +++ b/examples/randomness/src/lib.rs @@ -2,13 +2,10 @@ use bytemuck::cast; use stateroom_wasm::prelude::*; #[stateroom_wasm] +#[derive(Default)] struct RandomServer; -impl SimpleStateroomService for RandomServer { - fn new(_: &str, _: &impl StateroomContext) -> Self { - RandomServer - } - +impl StateroomService for RandomServer { fn connect(&mut self, client_id: ClientId, ctx: &impl StateroomContext) { let mut buf: [u8; 4] = [0, 0, 0, 0]; unsafe { diff --git a/stateroom-server/Cargo.toml b/stateroom-server/Cargo.toml index 9f2c716..629caa8 100644 --- a/stateroom-server/Cargo.toml +++ b/stateroom-server/Cargo.toml @@ -14,3 +14,4 @@ dashmap = "5.5.3" futures-util = "0.3.30" stateroom = {path="../stateroom", version="0.2.8"} tokio = { version = "1.37.0", features = ["rt-multi-thread"] } +tracing = "0.1.40" diff --git a/stateroom-server/src/lib.rs b/stateroom-server/src/lib.rs index 7c01699..de56358 100644 --- a/stateroom-server/src/lib.rs +++ b/stateroom-server/src/lib.rs @@ -4,8 +4,8 @@ use axum::{ routing::get, Router, }; -use server::{ServerState, ServiceActorContext}; -use stateroom::{StateroomService, StateroomServiceFactory}; +use server::ServerState; +use stateroom::StateroomServiceFactory; use std::{ net::{IpAddr, SocketAddr}, sync::Arc, @@ -103,17 +103,8 @@ impl Server { /// endpoints are available: /// - `/` (GET): return HTTP 200 if the server is running (useful as a baseline status check) /// - `/ws` (GET): initiate a WebSocket connection to the stateroom service. - pub async fn serve_async( - self, - service_factory: impl StateroomServiceFactory - + Send - + Sync - + 'static, - ) -> std::io::Result<()> - where - J: StateroomService + Send + Sync + Unpin + 'static, - { - let server_state = Arc::new(ServerState::new(service_factory)); + pub async fn serve_async(self, factory: impl StateroomServiceFactory) -> std::io::Result<()> { + let server_state = Arc::new(ServerState::new(factory)); let app = Router::new() .route("/ws", get(serve_websocket)) @@ -133,21 +124,12 @@ impl Server { /// endpoints are available: /// - `/` (GET): return HTTP 200 if the server is running (useful as a baseline status check) /// - `/ws` (GET): initiate a WebSocket connection to the stateroom service. - pub fn serve( - self, - service_factory: impl StateroomServiceFactory - + Send - + Sync - + 'static, - ) -> std::io::Result<()> - where - J: StateroomService + Send + Sync + Unpin + 'static, - { + pub fn serve(self, factory: impl StateroomServiceFactory) -> std::io::Result<()> { tokio::runtime::Builder::new_multi_thread() .enable_all() .build() .unwrap() - .block_on(async { self.serve_async(service_factory).await }) + .block_on(async { self.serve_async(factory).await }) } } diff --git a/stateroom-server/src/server.rs b/stateroom-server/src/server.rs index f1096ed..e1e0bc1 100644 --- a/stateroom-server/src/server.rs +++ b/stateroom-server/src/server.rs @@ -14,13 +14,12 @@ use tokio::{ /// A [StateroomContext] implementation for [StateroomService]s hosted in the /// context of a [ServiceActor]. -#[derive(Clone)] -pub struct ServiceActorContext { +pub struct ServerStateroomContext { senders: Arc>>, - event_sender: Sender, + event_sender: Arc>, } -impl ServiceActorContext { +impl ServerStateroomContext { pub fn try_send(&self, recipient: MessageRecipient, message: Message) { match recipient { MessageRecipient::Broadcast => { @@ -39,14 +38,14 @@ impl ServiceActorContext { if let Some(sender) = self.senders.get(&client_id) { sender.try_send(message).unwrap(); } else { - println!("No sender for client {:?}", client_id); + tracing::error!(?client_id, "No sender for client."); } } } } } -impl StateroomContext for ServiceActorContext { +impl StateroomContext for ServerStateroomContext { fn send_message(&self, recipient: impl Into, message: &str) { self.try_send(recipient.into(), Message::Text(message.to_string())); } @@ -60,7 +59,7 @@ impl StateroomContext for ServiceActorContext { let sender = self.event_sender.clone(); tokio::spawn(async move { tokio::time::sleep(Duration::from_millis(ms_delay as u64)).await; - sender.send(Event::TimerEvent).await.unwrap(); + sender.send(Event::Timer).await.unwrap(); }); } } @@ -78,13 +77,11 @@ pub enum Event { Message { client: ClientId, message: Message }, Join { client: ClientId }, Leave { client: ClientId }, - TimerEvent, + Timer, } impl ServerState { - pub fn new( - service_factory: impl StateroomServiceFactory + Send + 'static, - ) -> Self { + pub fn new(factory: impl StateroomServiceFactory) -> Self { let (tx, mut rx) = tokio::sync::mpsc::channel::(100); let senders = Arc::new(DashMap::new()); @@ -92,30 +89,27 @@ impl ServerState { let senders_ = senders.clone(); let tx_ = tx.clone(); let handle = tokio::spawn(async move { - let mut service = service_factory - .build( - "", - ServiceActorContext { - senders: senders_.clone(), - event_sender: tx_, - }, - ) - .unwrap(); - + let context = Arc::new(ServerStateroomContext { + senders: senders_.clone(), + event_sender: Arc::new(tx_), + }); + + let mut service = factory.build("", context.clone()).unwrap(); + service.init(context.as_ref()); + loop { let msg = rx.recv().await; - println!("{:?}", msg); match msg { Some(Event::Message { client, message }) => match message { - Message::Text(msg) => service.message(client, &msg), - Message::Binary(msg) => service.binary(client, &msg), + Message::Text(msg) => service.message(client, &msg, context.as_ref()), + Message::Binary(msg) => service.binary(client, &msg, context.as_ref()), Message::Close(_) => {} - msg => println!("Ignoring unhandled message: {:?}", msg), + msg => tracing::warn!("Ignoring unhandled message: {:?}", msg), }, - Some(Event::Join { client }) => service.connect(client), - Some(Event::Leave { client }) => service.disconnect(client), - Some(Event::TimerEvent) => { - service.timer(); + Some(Event::Join { client }) => service.connect(client, context.as_ref()), + Some(Event::Leave { client }) => service.disconnect(client, context.as_ref()), + Some(Event::Timer) => { + service.timer(context.as_ref()); } None => break, } @@ -133,7 +127,7 @@ impl ServerState { pub fn remove(&self, client: &ClientId) { self.inbound_sender .try_send(Event::Leave { - client: client.clone(), + client: *client, }) .unwrap(); self.senders.remove(client); diff --git a/stateroom-wasm-host/src/wasm_host.rs b/stateroom-wasm-host/src/wasm_host.rs index 3140dfb..2ab2cc7 100644 --- a/stateroom-wasm-host/src/wasm_host.rs +++ b/stateroom-wasm-host/src/wasm_host.rs @@ -11,6 +11,7 @@ const EXT_MEMORY: &str = "memory"; const EXT_FN_CONNECT: &str = "connect"; const EXT_FN_DISCONNECT: &str = "disconnect"; const EXT_FN_BINARY: &str = "binary"; +const EXT_FN_INIT: &str = "init"; const EXT_FN_MESSAGE: &str = "message"; const EXT_FN_SEND_MESSAGE: &str = "send_message"; const EXT_FN_SEND_BINARY: &str = "send_binary"; @@ -32,6 +33,7 @@ pub struct WasmHost { fn_malloc: TypedFunc, fn_free: TypedFunc<(u32, u32), ()>, + fn_init: TypedFunc<(), ()>, fn_message: TypedFunc<(u32, u32, u32), ()>, fn_binary: TypedFunc<(u32, u32, u32), ()>, fn_connect: TypedFunc, @@ -65,7 +67,7 @@ impl WasmHost { let (pt, len) = self.put_data(message)?; self.fn_binary - .call(&mut self.store, (client.into(), pt as u32, len))?; + .call(&mut self.store, (client.into(), pt, len))?; self.fn_free.call(&mut self.store, (pt, len))?; @@ -74,31 +76,37 @@ impl WasmHost { } impl StateroomService for WasmHost { - fn message(&mut self, client: ClientId, message: &str) { + fn init(&mut self, _: &impl StateroomContext) { + if let Err(error) = self.fn_init.call(&mut self.store, ()) { + tracing::error!(?error, "Error calling `init` on wasm host"); + } + } + + fn message(&mut self, client: ClientId, message: &str, _: &impl StateroomContext) { if let Err(error) = self.try_message(client, message) { tracing::error!(?error, "Error calling `message` on wasm host"); } } - fn connect(&mut self, client: ClientId) { + fn connect(&mut self, client: ClientId, _: &impl StateroomContext) { if let Err(error) = self.fn_connect.call(&mut self.store, client.into()) { tracing::error!(?error, "Error calling `connect` on wasm host"); } } - fn disconnect(&mut self, client: ClientId) { + fn disconnect(&mut self, client: ClientId, _: &impl StateroomContext) { if let Err(error) = self.fn_disconnect.call(&mut self.store, client.into()) { tracing::error!(?error, "Error calling `disconnect` on wasm host"); }; } - fn timer(&mut self) { + fn timer(&mut self, _: &impl StateroomContext) { if let Err(error) = self.fn_timer.call(&mut self.store, ()) { tracing::error!(?error, "Error calling `timer` on wasm host"); }; } - fn binary(&mut self, client: ClientId, message: &[u8]) { + fn binary(&mut self, client: ClientId, message: &[u8], _: &impl StateroomContext) { if let Err(error) = self.try_binary(client, message) { tracing::error!(?error, "Error calling `binary` on wasm host"); }; @@ -172,7 +180,7 @@ impl WasmHost { room_id: &str, module: &Module, engine: &Engine, - context: &Arc, + context: Arc, ) -> Result { let wasi = WasiCtxBuilder::new().inherit_stdio().build(); @@ -271,6 +279,8 @@ impl WasmHost { let fn_timer = instance.get_typed_func::<(), ()>(&mut store, EXT_FN_TIMER)?; + let fn_init = instance.get_typed_func::<(), ()>(&mut store, EXT_FN_INIT)?; + let fn_message = instance.get_typed_func::<(u32, u32, u32), ()>(&mut store, EXT_FN_MESSAGE)?; @@ -282,6 +292,7 @@ impl WasmHost { memory, fn_malloc, fn_free, + fn_init, fn_message, fn_binary, fn_connect, diff --git a/stateroom-wasm-host/src/wasm_host_factory.rs b/stateroom-wasm-host/src/wasm_host_factory.rs index 56a0fe8..c0a3ebe 100644 --- a/stateroom-wasm-host/src/wasm_host_factory.rs +++ b/stateroom-wasm-host/src/wasm_host_factory.rs @@ -15,17 +15,16 @@ pub struct WasmHostFactory { module: Arc, } -impl StateroomServiceFactory for WasmHostFactory { +impl StateroomServiceFactory for WasmHostFactory { type Service = WasmHost; type Error = anyhow::Error; - fn build(&self, room_id: &str, context: T) -> Result { - WasmHost::new( - room_id, - self.module.as_ref(), - self.engine.as_ref(), - &Arc::new(context), - ) + fn build( + &self, + room_id: &str, + context: Arc, + ) -> Result { + WasmHost::new(room_id, self.module.as_ref(), self.engine.as_ref(), context) } } diff --git a/stateroom-wasm/src/prelude.rs b/stateroom-wasm/src/prelude.rs index b9ec4b9..541518d 100644 --- a/stateroom-wasm/src/prelude.rs +++ b/stateroom-wasm/src/prelude.rs @@ -1,6 +1,3 @@ /// Re-exports useful items from `stateroom` and `stateroom_wasm_macro`. -pub use stateroom::{ - ClientId, MessageRecipient, SimpleStateroomService, StateroomContext, StateroomService, - StateroomServiceFactory, WrappedStateroomService, -}; +pub use stateroom::{ClientId, MessageRecipient, StateroomContext, StateroomService}; pub use stateroom_wasm_macro::stateroom_wasm; diff --git a/stateroom-wasm/stateroom-wasm-macro/src/lib.rs b/stateroom-wasm/stateroom-wasm-macro/src/lib.rs index cfcabf5..f73c887 100644 --- a/stateroom-wasm/stateroom-wasm-macro/src/lib.rs +++ b/stateroom-wasm/stateroom-wasm-macro/src/lib.rs @@ -32,7 +32,6 @@ fn stateroom_wasm_impl(item: &proc_macro2::TokenStream) -> proc_macro2::TokenStr use super::*; use stateroom_wasm::prelude::{ MessageRecipient, - SimpleStateroomService, StateroomContext, ClientId, }; @@ -93,7 +92,7 @@ fn stateroom_wasm_impl(item: &proc_macro2::TokenStream) -> proc_macro2::TokenStr let room_id = unsafe { String::from_utf8(std::slice::from_raw_parts(room_id_ptr, room_id_len).to_vec()).map_err(|e| format!("Error parsing UTF-8 from host {:?}", e)).unwrap() }; - let mut c = #name::new(&room_id, &GlobalStateroomContext); + let mut c = #name::default(); unsafe { SERVER_STATE.replace(c); @@ -103,7 +102,7 @@ fn stateroom_wasm_impl(item: &proc_macro2::TokenStream) -> proc_macro2::TokenStr #[no_mangle] extern "C" fn connect(client_id: ClientId) { match unsafe { SERVER_STATE.as_mut() } { - Some(st) => SimpleStateroomService::connect(st, client_id.into(), &GlobalStateroomContext), + Some(st) => StateroomService::connect(st, client_id.into(), &GlobalStateroomContext), None => () } } @@ -111,7 +110,7 @@ fn stateroom_wasm_impl(item: &proc_macro2::TokenStream) -> proc_macro2::TokenStr #[no_mangle] extern "C" fn disconnect(client_id: ClientId) { match unsafe { SERVER_STATE.as_mut() } { - Some(st) => SimpleStateroomService::disconnect(st, client_id.into(), &GlobalStateroomContext), + Some(st) => StateroomService::disconnect(st, client_id.into(), &GlobalStateroomContext), None => () } } @@ -119,7 +118,15 @@ fn stateroom_wasm_impl(item: &proc_macro2::TokenStream) -> proc_macro2::TokenStr #[no_mangle] extern "C" fn timer() { match unsafe { SERVER_STATE.as_mut() } { - Some(st) => SimpleStateroomService::timer(st, &GlobalStateroomContext), + Some(st) => StateroomService::timer(st, &GlobalStateroomContext), + None => () + } + } + + #[no_mangle] + extern "C" fn init() { + match unsafe { SERVER_STATE.as_mut() } { + Some(st) => StateroomService::init(st, &GlobalStateroomContext), None => () } } @@ -130,7 +137,7 @@ fn stateroom_wasm_impl(item: &proc_macro2::TokenStream) -> proc_macro2::TokenStr let string = String::from_utf8(std::slice::from_raw_parts(ptr, len).to_vec()).expect("Error parsing UTF-8 from host {:?}"); match SERVER_STATE.as_mut() { - Some(st) => SimpleStateroomService::message(st, client_id.into(), &string, &GlobalStateroomContext), + Some(st) => StateroomService::message(st, client_id.into(), &string, &GlobalStateroomContext), None => () } } @@ -142,7 +149,7 @@ fn stateroom_wasm_impl(item: &proc_macro2::TokenStream) -> proc_macro2::TokenStr let data = std::slice::from_raw_parts(ptr, len); match SERVER_STATE.as_mut() { - Some(st) => SimpleStateroomService::binary(st, client_id.into(), data, &GlobalStateroomContext), + Some(st) => StateroomService::binary(st, client_id.into(), data, &GlobalStateroomContext), None => () } } @@ -169,7 +176,7 @@ fn stateroom_wasm_impl(item: &proc_macro2::TokenStream) -> proc_macro2::TokenStr } } -/// Exposes a `stateroom_wasm::SimpleStateroomService`-implementing trait as a WebAssembly module. +/// Exposes a `stateroom_wasm::StateroomService`-implementing trait as a WebAssembly module. #[proc_macro_attribute] pub fn stateroom_wasm(_attr: TokenStream, item: TokenStream) -> TokenStream { #[allow(clippy::needless_borrow)] diff --git a/stateroom/src/lib.rs b/stateroom/src/lib.rs index 8ee9529..6c17d79 100644 --- a/stateroom/src/lib.rs +++ b/stateroom/src/lib.rs @@ -17,11 +17,7 @@ //! client_to_nickname: HashMap, //! } //! -//! impl SimpleStateroomService for ChatServer { -//! fn new(room_id: &str, _: &impl StateroomContext) -> Self { -//! Default::default() -//! } -//! +//! impl StateroomService for ChatServer { //! /// This is called when a user connects. //! fn connect(&mut self, client: ClientId, ctx: &impl StateroomContext) { //! let username = format!("client{}", u32::from(client)); @@ -62,17 +58,18 @@ //! } //! } +use std::sync::Arc; + pub use client_id::ClientId; pub use message_recipient::MessageRecipient; pub use messages::{MessageFromProcess, MessagePayload, MessageToProcess}; -use std::convert::Infallible; mod client_id; mod message_recipient; mod messages; /// Provides an interface for a [StateroomService] instance to send messages back to its host environment. -pub trait StateroomContext { +pub trait StateroomContext: Send + Sync + 'static { /// Sends a message to a currently connected user, or broadcast a message to all users. /// /// Recipient can be a `u32` representing an individual user to send a message to, or @@ -99,9 +96,9 @@ pub trait StateroomContext { /// /// See module documentation for usage examples. #[allow(unused_variables)] -pub trait SimpleStateroomService { +pub trait StateroomService: Send + Sync + 'static { /// Called when the service is created, before any client has had a chance to connect. - fn new(room_id: &str, context: &impl StateroomContext) -> Self; + fn init(&mut self, context: &impl StateroomContext) {} /// Called each time a client connects to the service. fn connect(&mut self, client: ClientId, context: &impl StateroomContext) {} @@ -121,80 +118,15 @@ pub trait SimpleStateroomService { fn timer(&mut self, context: &impl StateroomContext) {} } -/// The host interface to a Stateroom service. Implementations should instead implement the trait -/// [SimpleStateroomService]. -#[allow(unused_variables)] -pub trait StateroomService { - /// Called each time a client connects to the service. - fn connect(&mut self, client: ClientId) {} - - /// Called each time a client disconnects from the service, unless that disconnection - /// will cause the service to be destroyed. - fn disconnect(&mut self, client: ClientId) {} - - /// Called each time a client sends a text message to the service. - fn message(&mut self, client: ClientId, message: &str) {} - - /// Called each time a client sends a binary message to the service. - fn binary(&mut self, client: ClientId, message: &[u8]) {} - - /// Called when [StateroomContext::set_timer] has been called on this service's context, - /// after the provided duration. - fn timer(&mut self) {} -} - -/// Enables an object to become a [StateroomService] of the associated `Service` type. -pub trait StateroomServiceFactory { +pub trait StateroomServiceFactory: Send + Sync + 'static { /// The type of [StateroomService] that the object implementing this trait builds. type Service: StateroomService; type Error: std::fmt::Debug; /// Non-destructively build a [StateroomService] from `self`. - fn build(&self, room_id: &str, context: C) -> Result; -} - -impl StateroomServiceFactory for S { - type Service = WrappedStateroomService; - type Error = Infallible; - - fn build(&self, _room_id: &str, context: C) -> Result { - Ok(WrappedStateroomService::new(self.clone(), context)) - } -} - -/// Combines a [SimpleStateroomService] with an owned [StateroomContext] in order to implement -/// [StateroomService]. -pub struct WrappedStateroomService { - service: S, - context: C, -} - -impl WrappedStateroomService { - pub fn new(service: S, context: C) -> Self { - WrappedStateroomService { service, context } - } -} - -impl StateroomService - for WrappedStateroomService -{ - fn connect(&mut self, client: ClientId) { - self.service.connect(client, &self.context); - } - - fn disconnect(&mut self, client: ClientId) { - self.service.disconnect(client, &self.context); - } - - fn message(&mut self, client: ClientId, message: &str) { - self.service.message(client, message, &self.context); - } - - fn timer(&mut self) { - self.service.timer(&self.context); - } - - fn binary(&mut self, client: ClientId, message: &[u8]) { - self.service.binary(client, message, &self.context); - } + fn build( + &self, + room_id: &str, + context: Arc, + ) -> Result; }