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..7c914b1 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; @@ -121,4 +123,17 @@ pub trait Composite { } vec } + + /// 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 + where + Self: Sized, + { + Arc::new(RwLock::new(self)) + } } diff --git a/tests/common/mod.rs b/tests/common/mod.rs index bce419a..a267125 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::Composite; 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..deb3129 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, Composite, RoleCreateModifySchema, RoleObject}; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; #[cfg(target_arch = "wasm32")] @@ -97,11 +95,7 @@ async fn test_recursive_self_updating_structs() { .await .unwrap(); // Watch role; - bundle - .user - .gateway - .observe(Arc::new(RwLock::new(role.clone()))) - .await; + bundle.user.gateway.observe(role.into_shared()).await; // Update Guild and check for Guild let inner_guild = guild.read().unwrap().clone(); assert!(inner_guild.roles.is_some()); @@ -113,7 +107,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.into_shared()) .await; assert_eq!(role_inner.name, "yippieee"); // Check if the change propagated