diff --git a/examples/gateway_simple.rs b/examples/gateway_simple.rs index affa850..7a0d807 100644 --- a/examples/gateway_simple.rs +++ b/examples/gateway_simple.rs @@ -31,8 +31,8 @@ async fn main() { identify.token = token; // Send off the event - gateway.send_identify(identify).await; - + gateway.send_identify(identify).await; + // Do something on the main thread so we don't quit loop { sleep(Duration::from_secs(3600)).await; diff --git a/src/gateway/handle.rs b/src/gateway/handle.rs index 620faba..bc64077 100644 --- a/src/gateway/handle.rs +++ b/src/gateway/handle.rs @@ -40,10 +40,19 @@ impl GatewayHandle { .unwrap(); } + /// Recursively observes a [`Shared`] object, by making sure all [`Composite `] fields within + /// that object and its children are being watched. + /// + /// Observing means, that if new information arrives about the observed object or its children, + /// the object automatically gets updated, without you needing to request new information about + /// the object in question from the API, which is expensive and can lead to rate limiting. + /// + /// The [`Shared`] object returned by this method points to a different object than the one + /// being supplied as a &self function argument. pub async fn observe>( &self, - object: Arc>, - ) -> Arc> { + object: Shared, + ) -> Shared { let mut store = self.store.lock().await; let id = object.read().unwrap().id(); if let Some(channel) = store.get(&id) { @@ -84,7 +93,7 @@ impl GatewayHandle { /// with all of its observable fields being observed. pub async fn observe_and_into_inner>( &self, - object: Arc>, + object: Shared, ) -> T { let channel = self.observe(object.clone()).await; let object = channel.read().unwrap().clone(); diff --git a/src/gateway/heartbeat.rs b/src/gateway/heartbeat.rs index 8e37697..e6991f3 100644 --- a/src/gateway/heartbeat.rs +++ b/src/gateway/heartbeat.rs @@ -71,7 +71,7 @@ impl HeartbeatHandler { let mut last_heartbeat_timestamp: Instant = Instant::now(); let mut last_heartbeat_acknowledged = true; let mut last_seq_number: Option = None; - + loop { if kill_receive.try_recv().is_ok() { trace!("GW: Closing heartbeat task"); diff --git a/src/gateway/mod.rs b/src/gateway/mod.rs index 076ed54..c5f415e 100644 --- a/src/gateway/mod.rs +++ b/src/gateway/mod.rs @@ -122,3 +122,11 @@ impl GatewayEvent { } } } + +/// A type alias for [`Arc>`], used to make the public facing API concerned with +/// Composite structs more ergonomic. +/// ## Note +/// +/// While `T` does not have to implement `Composite` to be used with `Shared`, +/// the primary use of `Shared` is with types that implement `Composite`. +pub type Shared = Arc>; diff --git a/src/types/entities/mod.rs b/src/types/entities/mod.rs index 8343628..5ceada6 100644 --- a/src/types/entities/mod.rs +++ b/src/types/entities/mod.rs @@ -23,6 +23,8 @@ pub use user_settings::*; pub use voice_state::*; pub use webhook::*; +use crate::gateway::Shared; + #[cfg(feature = "client")] use crate::gateway::Updateable; @@ -69,9 +71,9 @@ pub trait Composite { async fn watch_whole(self, gateway: &GatewayHandle) -> Self; async fn option_observe_fn( - value: Option>>, + value: Option>, gateway: &GatewayHandle, - ) -> Option>> + ) -> Option> where T: Composite + Debug, { @@ -84,9 +86,9 @@ pub trait Composite { } async fn option_vec_observe_fn( - value: Option>>>, + value: Option>>, gateway: &GatewayHandle, - ) -> Option>>> + ) -> Option>> where T: Composite, { @@ -101,17 +103,14 @@ pub trait Composite { } } - async fn value_observe_fn(value: Arc>, gateway: &GatewayHandle) -> Arc> + async fn value_observe_fn(value: Shared, gateway: &GatewayHandle) -> Shared where T: Composite, { gateway.observe(value).await } - async fn vec_observe_fn( - value: Vec>>, - gateway: &GatewayHandle, - ) -> Vec>> + async fn vec_observe_fn(value: Vec>, gateway: &GatewayHandle) -> Vec> where T: Composite, { @@ -122,3 +121,19 @@ pub trait Composite { vec } } + +pub trait IntoShared { + /// Uses [`Shared`] to provide an ergonomic alternative to `Arc::new(RwLock::new(obj))`. + /// + /// [`Shared`] can then be observed using the [`Gateway`], turning the underlying + /// `dyn Composite` into a self-updating struct, which is a tracked variant of a chorus + /// entity struct, updating its' held information when new information concerning itself arrives + /// over the [`Gateway`] connection, reducing the need for expensive network-API calls. + fn into_shared(self) -> Shared; +} + +impl IntoShared for T { + fn into_shared(self) -> Shared { + Arc::new(RwLock::new(self)) + } +} diff --git a/tests/common/mod.rs b/tests/common/mod.rs index bce419a..e18f92b 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,6 +1,5 @@ -use std::sync::{Arc, RwLock}; - -use chorus::gateway::Gateway; +use chorus::gateway::{Gateway, Shared}; +use chorus::types::IntoShared; use chorus::{ instance::{ChorusUser, Instance}, types::{ @@ -16,9 +15,9 @@ pub(crate) struct TestBundle { pub urls: UrlBundle, pub user: ChorusUser, pub instance: Instance, - pub guild: Arc>, - pub role: Arc>, - pub channel: Arc>, + pub guild: Shared, + pub role: Shared, + pub channel: Shared, } #[allow(unused)] @@ -119,9 +118,9 @@ pub(crate) async fn setup() -> TestBundle { urls, user, instance, - guild: Arc::new(RwLock::new(guild)), - role: Arc::new(RwLock::new(role)), - channel: Arc::new(RwLock::new(channel)), + guild: guild.into_shared(), + role: role.into_shared(), + channel: channel.into_shared(), } } diff --git a/tests/gateway.rs b/tests/gateway.rs index 5bf5865..66259f7 100644 --- a/tests/gateway.rs +++ b/tests/gateway.rs @@ -1,10 +1,8 @@ mod common; -use std::sync::{Arc, RwLock}; - use chorus::errors::GatewayError; use chorus::gateway::*; -use chorus::types::{self, ChannelModifySchema, RoleCreateModifySchema, RoleObject}; +use chorus::types::{self, ChannelModifySchema, IntoShared, RoleCreateModifySchema, RoleObject}; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; #[cfg(target_arch = "wasm32")] @@ -100,7 +98,7 @@ async fn test_recursive_self_updating_structs() { bundle .user .gateway - .observe(Arc::new(RwLock::new(role.clone()))) + .observe(role.clone().into_shared()) .await; // Update Guild and check for Guild let inner_guild = guild.read().unwrap().clone(); @@ -113,7 +111,7 @@ async fn test_recursive_self_updating_structs() { let role_inner = bundle .user .gateway - .observe_and_into_inner(Arc::new(RwLock::new(role.clone()))) + .observe_and_into_inner(role.clone().into_shared()) .await; assert_eq!(role_inner.name, "yippieee"); // Check if the change propagated