Compare commits

...

36 Commits

Author SHA1 Message Date
kozabrada123 93d32944b1 merge with dev 2024-01-26 20:03:57 +01:00
Flori 7a7c468bd0
Coverage (#468)
Up the reported coverage on coveralls by adding some missing tests,
especially for types. Also removes/replaces some old, faulty or
non-idiomatic code.

While unit testing might seem a bit bloaty (especially with this many
additions), I'd argue that writing somewhat sensible tests is the only
good way to somewhat reliably keep things from breaking in larger
projects/bigger codebases, such as these.

Most of these tests should, hopefully, be of acceptable quality,
although I do admit that when writing tests, I sometimes just like to
turn my brain off
2024-01-24 23:44:31 +01:00
bitfl0wer 262a52a8e9
exclude trivial id() functions from coverage 2024-01-24 23:26:59 +01:00
bitfl0wer 3040dcc46b
Add comment about test_self_updating_structs 2024-01-24 23:02:16 +01:00
bitfl0wer c950288df1
extend self updating structs test 2024-01-24 23:01:38 +01:00
bitfl0wer e073ff26c4
remove hit limit test 2024-01-24 18:51:10 +01:00
bitfl0wer 3ffb124cd4
Add test for get_limit_config 2024-01-24 12:32:08 +01:00
bitfl0wer 57e6cb438d
Add test to hit ratelimit 2024-01-24 12:21:46 +01:00
bitfl0wer 970f5b8b4f
Remove PartialOrd from Emoji because unneccessary 2024-01-23 23:53:08 +01:00
bitfl0wer 98f42aa03b
Add Message PartialEq Test 2024-01-23 23:42:00 +01:00
bitfl0wer 9cc7ede763
Add partial_eq test for relationship.rs/Relationship 2024-01-23 23:31:35 +01:00
bitfl0wer a2b6d4e407
Remove old/redundant code from attachment.rs 2024-01-23 23:11:37 +01:00
bitfl0wer 41a0e2fe27
Add to_public_user test 2024-01-23 23:06:24 +01:00
bitfl0wer 00c70501c4
Rename to_public_user into into_public_user 2024-01-23 23:06:14 +01:00
bitfl0wer 97ab757633
Add unit tests for guild.rs entities 2024-01-23 21:13:15 +01:00
bitfl0wer 7434690027
Remove Eq fromn Guild as it is not Eq 2024-01-23 21:05:01 +01:00
bitfl0wer 577a399a7b
Create tests from to_str and from_str for GuildFeatures 2024-01-23 20:50:19 +01:00
bitfl0wer 11df180446
APPEND: Remove unused imports 2024-01-23 19:08:35 +01:00
bitfl0wer 0923de59a4
Replace usage of Arc<RwLock<...>> in public APIs with Shared<...> 2024-01-23 19:07:23 +01:00
bitfl0wer 8846159ffd
Remove impl Eq from types that didn't qualify for Eq 2024-01-23 18:43:06 +01:00
bitfl0wer 013687c810
write unit tests for config and entities 2024-01-22 20:57:29 +01:00
bitfl0wer c6e7724650
rustfmt 2024-01-22 20:57:17 +01:00
bitfl0wer 74fc954d1a
Fix broken behaviour for ConfigEntity 2024-01-22 20:57:08 +01:00
Flori fe8106d2a1
"Self updating structs" API improvements (#467)
This PR slightly improves the ergonomics of working with self-updating
structs, by making the changes as documented in #466.
2024-01-22 15:19:24 +01:00
bitfl0wer 21699e5899
Loosen bounds on IntoShared<T> 2024-01-22 15:00:46 +01:00
bitfl0wer 5372d2c475
Make IntoShared trait with blanket implementation 2024-01-22 14:56:23 +01:00
bitfl0wer 29f3ee802a
Fix errors by moving into_shared out of Composite 2024-01-22 14:50:33 +01:00
bitfl0wer 6637f14b18
Replace Arc, Rwlock with Shared 2024-01-21 20:24:17 +01:00
bitfl0wer a571a9e137
Write documentation for observe 2024-01-21 20:13:00 +01:00
bitfl0wer 57214fd2fe
Add documentation for into_shared 2024-01-21 17:15:11 +01:00
bitfl0wer ca58767372
Rename to_shared to into_shared 2024-01-21 17:10:24 +01:00
bitfl0wer 2a7cae30b8
Define public method `to_shared` for dyn Composite 2024-01-21 17:07:54 +01:00
bitfl0wer 0660e25bdb
rustfmt 2024-01-21 17:07:30 +01:00
bitfl0wer 36ac6c1e5e
Replace use of Arc<RwLock<T>> with Shared<T> 2024-01-21 17:07:19 +01:00
bitfl0wer 315fe8e33b
Define type alias `Shared` 2024-01-21 17:06:43 +01:00
kozabrada123 8e25f401a5
Minor instance updates (#465)
make Instance::from_url_bundle pub, update Instance docs
2024-01-20 13:15:13 +01:00
37 changed files with 1322 additions and 254 deletions

View File

@ -36,7 +36,7 @@ impl Message {
chorus_request.deserialize_response::<Message>(user).await chorus_request.deserialize_response::<Message>(user).await
} else { } else {
for (index, attachment) in message.attachments.iter_mut().enumerate() { for (index, attachment) in message.attachments.iter_mut().enumerate() {
attachment.get_mut(index).unwrap().set_id(index as i16); attachment.get_mut(index).unwrap().id = Some(index as i16);
} }
let mut form = reqwest::multipart::Form::new(); let mut form = reqwest::multipart::Form::new();
let payload_json = to_string(&message).unwrap(); let payload_json = to_string(&message).unwrap();
@ -45,8 +45,8 @@ impl Message {
form = form.part("payload_json", payload_field); form = form.part("payload_json", payload_field);
for (index, attachment) in message.attachments.unwrap().into_iter().enumerate() { for (index, attachment) in message.attachments.unwrap().into_iter().enumerate() {
let (attachment_content, current_attachment) = attachment.move_content(); let attachment_content = attachment.content;
let (attachment_filename, _) = current_attachment.move_filename(); let attachment_filename = attachment.filename;
let part_name = format!("files[{}]", index); let part_name = format!("files[{}]", index);
let content_disposition = format!( let content_disposition = format!(
"form-data; name=\"{}\"'; filename=\"{}\"", "form-data; name=\"{}\"'; filename=\"{}\"",

View File

@ -40,10 +40,19 @@ impl GatewayHandle {
.unwrap(); .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<T: Updateable + Clone + Debug + Composite<T>>( pub async fn observe<T: Updateable + Clone + Debug + Composite<T>>(
&self, &self,
object: Arc<RwLock<T>>, object: Shared<T>,
) -> Arc<RwLock<T>> { ) -> Shared<T> {
let mut store = self.store.lock().await; let mut store = self.store.lock().await;
let id = object.read().unwrap().id(); let id = object.read().unwrap().id();
if let Some(channel) = store.get(&id) { if let Some(channel) = store.get(&id) {
@ -84,7 +93,7 @@ impl GatewayHandle {
/// with all of its observable fields being observed. /// with all of its observable fields being observed.
pub async fn observe_and_into_inner<T: Updateable + Clone + Debug + Composite<T>>( pub async fn observe_and_into_inner<T: Updateable + Clone + Debug + Composite<T>>(
&self, &self,
object: Arc<RwLock<T>>, object: Shared<T>,
) -> T { ) -> T {
let channel = self.observe(object.clone()).await; let channel = self.observe(object.clone()).await;
let object = channel.read().unwrap().clone(); let object = channel.read().unwrap().clone();

View File

@ -128,3 +128,11 @@ impl<T: WebSocketEvent> GatewayEvent<T> {
} }
} }
} }
/// A type alias for [`Arc<RwLock<T>>`], 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<T> = Arc<RwLock<T>>;

View File

@ -9,7 +9,7 @@ use reqwest::Client;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::errors::ChorusResult; use crate::errors::ChorusResult;
use crate::gateway::{Gateway, GatewayHandle}; use crate::gateway::{Gateway, GatewayHandle, Shared};
use crate::ratelimiter::ChorusRequest; use crate::ratelimiter::ChorusRequest;
use crate::types::types::subconfigs::limits::rates::RateLimits; use crate::types::types::subconfigs::limits::rates::RateLimits;
use crate::types::{ use crate::types::{
@ -19,6 +19,7 @@ use crate::UrlBundle;
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
/// The [`Instance`]; what you will be using to perform all sorts of actions on the Spacebar server. /// The [`Instance`]; what you will be using to perform all sorts of actions on the Spacebar server.
///
/// If `limits_information` is `None`, then the instance will not be rate limited. /// If `limits_information` is `None`, then the instance will not be rate limited.
pub struct Instance { pub struct Instance {
pub urls: UrlBundle, pub urls: UrlBundle,
@ -36,8 +37,6 @@ impl PartialEq for Instance {
} }
} }
impl Eq for Instance {}
impl std::hash::Hash for Instance { impl std::hash::Hash for Instance {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.urls.hash(state); self.urls.hash(state);
@ -72,8 +71,17 @@ impl PartialEq for LimitsInformation {
} }
impl Instance { impl Instance {
/// Creates a new [`Instance`] from the [relevant instance urls](UrlBundle). To create an Instance from one singular url, use [`Instance::from_root_url()`]. pub(crate) fn clone_limits_if_some(&self) -> Option<HashMap<LimitType, Limit>> {
async fn from_url_bundle(urls: UrlBundle) -> ChorusResult<Instance> { if self.limits_information.is_some() {
return Some(self.limits_information.as_ref().unwrap().ratelimits.clone());
}
None
}
/// Creates a new [`Instance`] from the [relevant instance urls](UrlBundle).
///
/// To create an Instance from one singular url, use [`Instance::new()`].
pub async fn from_url_bundle(urls: UrlBundle) -> ChorusResult<Instance> {
let is_limited: Option<LimitsConfiguration> = Instance::is_limited(&urls.api).await?; let is_limited: Option<LimitsConfiguration> = Instance::is_limited(&urls.api).await?;
let limit_information; let limit_information;
@ -103,17 +111,9 @@ impl Instance {
Ok(instance) Ok(instance)
} }
pub(crate) fn clone_limits_if_some(&self) -> Option<HashMap<LimitType, Limit>> {
if self.limits_information.is_some() {
return Some(self.limits_information.as_ref().unwrap().ratelimits.clone());
}
None
}
/// Creates a new [`Instance`] by trying to get the [relevant instance urls](UrlBundle) from a root url. /// Creates a new [`Instance`] by trying to get the [relevant instance urls](UrlBundle) from a root url.
/// Shorthand for `Instance::new(UrlBundle::from_root_domain(root_domain).await?)`.
/// ///
/// If `limited` is `true`, then Chorus will track and enforce rate limits for this instance. /// Shorthand for `Instance::from_url_bundle(UrlBundle::from_root_domain(root_domain).await?)`.
pub async fn new(root_url: &str) -> ChorusResult<Instance> { pub async fn new(root_url: &str) -> ChorusResult<Instance> {
let urls = UrlBundle::from_root_url(root_url).await?; let urls = UrlBundle::from_root_url(root_url).await?;
Instance::from_url_bundle(urls).await Instance::from_url_bundle(urls).await
@ -153,11 +153,11 @@ impl fmt::Display for Token {
/// It is used for most authenticated actions on a Spacebar server. /// It is used for most authenticated actions on a Spacebar server.
/// It also has its own [Gateway] connection. /// It also has its own [Gateway] connection.
pub struct ChorusUser { pub struct ChorusUser {
pub belongs_to: Arc<RwLock<Instance>>, pub belongs_to: Shared<Instance>,
pub token: String, pub token: String,
pub limits: Option<HashMap<LimitType, Limit>>, pub limits: Option<HashMap<LimitType, Limit>>,
pub settings: Arc<RwLock<UserSettings>>, pub settings: Shared<UserSettings>,
pub object: Arc<RwLock<User>>, pub object: Shared<User>,
pub gateway: GatewayHandle, pub gateway: GatewayHandle,
} }
@ -169,8 +169,6 @@ impl PartialEq for ChorusUser {
} }
} }
impl Eq for ChorusUser {}
impl ChorusUser { impl ChorusUser {
pub fn token(&self) -> String { pub fn token(&self) -> String {
self.token.clone() self.token.clone()
@ -186,11 +184,11 @@ impl ChorusUser {
/// This isn't the prefered way to create a ChorusUser. /// This isn't the prefered way to create a ChorusUser.
/// See [Instance::login_account] and [Instance::register_account] instead. /// See [Instance::login_account] and [Instance::register_account] instead.
pub fn new( pub fn new(
belongs_to: Arc<RwLock<Instance>>, belongs_to: Shared<Instance>,
token: String, token: String,
limits: Option<HashMap<LimitType, Limit>>, limits: Option<HashMap<LimitType, Limit>>,
settings: Arc<RwLock<UserSettings>>, settings: Shared<UserSettings>,
object: Arc<RwLock<User>>, object: Shared<User>,
gateway: GatewayHandle, gateway: GatewayHandle,
) -> ChorusUser { ) -> ChorusUser {
ChorusUser { ChorusUser {
@ -208,7 +206,7 @@ impl ChorusUser {
/// registering or logging in to the Instance, where you do not yet have a User object, but still /// registering or logging in to the Instance, where you do not yet have a User object, but still
/// need to make a RateLimited request. To use the [`GatewayHandle`], you will have to identify /// need to make a RateLimited request. To use the [`GatewayHandle`], you will have to identify
/// first. /// first.
pub(crate) async fn shell(instance: Arc<RwLock<Instance>>, token: String) -> ChorusUser { pub(crate) async fn shell(instance: Shared<Instance>, token: String) -> ChorusUser {
let settings = Arc::new(RwLock::new(UserSettings::default())); let settings = Arc::new(RwLock::new(UserSettings::default()));
let object = Arc::new(RwLock::new(User::default())); let object = Arc::new(RwLock::new(User::default()));
let wss_url = instance.read().unwrap().urls.wss.clone(); let wss_url = instance.read().unwrap().urls.wss.clone();

View File

@ -1,10 +1,9 @@
use std::sync::{Arc, RwLock};
use bitflags::bitflags; use bitflags::bitflags;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
use serde_repr::{Deserialize_repr, Serialize_repr}; use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::gateway::Shared;
use crate::types::utils::Snowflake; use crate::types::utils::Snowflake;
use crate::types::{Team, User}; use crate::types::{Team, User};
@ -27,7 +26,7 @@ pub struct Application {
pub bot_require_code_grant: bool, pub bot_require_code_grant: bool,
pub verify_key: String, pub verify_key: String,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub owner: Arc<RwLock<User>>, pub owner: Shared<User>,
pub flags: u64, pub flags: u64,
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]
pub redirect_uris: Option<sqlx::types::Json<Vec<String>>>, pub redirect_uris: Option<sqlx::types::Json<Vec<String>>>,
@ -49,7 +48,7 @@ pub struct Application {
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]
pub install_params: Option<sqlx::types::Json<InstallParams>>, pub install_params: Option<sqlx::types::Json<InstallParams>>,
#[cfg(not(feature = "sqlx"))] #[cfg(not(feature = "sqlx"))]
pub install_params: Option<Arc<RwLock<InstallParams>>>, pub install_params: Option<Shared<InstallParams>>,
pub terms_of_service_url: Option<String>, pub terms_of_service_url: Option<String>,
pub privacy_policy_url: Option<String>, pub privacy_policy_url: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
@ -142,7 +141,7 @@ pub struct ApplicationCommand {
pub application_id: Snowflake, pub application_id: Snowflake,
pub name: String, pub name: String,
pub description: String, pub description: String,
pub options: Vec<Arc<RwLock<ApplicationCommandOption>>>, pub options: Vec<Shared<ApplicationCommandOption>>,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@ -154,7 +153,7 @@ pub struct ApplicationCommandOption {
pub description: String, pub description: String,
pub required: bool, pub required: bool,
pub choices: Vec<ApplicationCommandOptionChoice>, pub choices: Vec<ApplicationCommandOptionChoice>,
pub options: Arc<RwLock<Vec<ApplicationCommandOption>>>, pub options: Shared<Vec<ApplicationCommandOption>>,
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@ -190,14 +189,14 @@ pub enum ApplicationCommandOptionType {
pub struct ApplicationCommandInteractionData { pub struct ApplicationCommandInteractionData {
pub id: Snowflake, pub id: Snowflake,
pub name: String, pub name: String,
pub options: Vec<Arc<RwLock<ApplicationCommandInteractionDataOption>>>, pub options: Vec<Shared<ApplicationCommandInteractionDataOption>>,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApplicationCommandInteractionDataOption { pub struct ApplicationCommandInteractionDataOption {
pub name: String, pub name: String,
pub value: Value, pub value: Value,
pub options: Vec<Arc<RwLock<ApplicationCommandInteractionDataOption>>>, pub options: Vec<Shared<ApplicationCommandInteractionDataOption>>,
} }
#[derive(Debug, Default, Clone, Serialize, Deserialize)] #[derive(Debug, Default, Clone, Serialize, Deserialize)]
@ -206,7 +205,7 @@ pub struct GuildApplicationCommandPermissions {
pub id: Snowflake, pub id: Snowflake,
pub application_id: Snowflake, pub application_id: Snowflake,
pub guild_id: Snowflake, pub guild_id: Snowflake,
pub permissions: Vec<Arc<RwLock<ApplicationCommandPermission>>>, pub permissions: Vec<Shared<ApplicationCommandPermission>>,
} }
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]

View File

@ -55,73 +55,3 @@ pub struct PartialDiscordFileAttachment {
#[serde(skip_serializing)] #[serde(skip_serializing)]
pub content: Vec<u8>, pub content: Vec<u8>,
} }
impl PartialDiscordFileAttachment {
/// Moves `self.content` out of `self` and returns it.
pub fn move_content(self) -> (Vec<u8>, PartialDiscordFileAttachment) {
let content = self.content;
let updated_struct = PartialDiscordFileAttachment {
id: self.id,
filename: self.filename,
description: self.description,
content_type: self.content_type,
size: self.size,
url: self.url,
proxy_url: self.proxy_url,
height: self.height,
width: self.width,
ephemeral: self.ephemeral,
duration_secs: self.duration_secs,
waveform: self.waveform,
content: Vec::new(),
};
(content, updated_struct)
}
/// Moves `self.filename` out of `self` and returns it.
pub fn move_filename(self) -> (String, PartialDiscordFileAttachment) {
let filename = self.filename;
let updated_struct = PartialDiscordFileAttachment {
id: self.id,
filename: String::new(),
description: self.description,
content_type: self.content_type,
size: self.size,
url: self.url,
proxy_url: self.proxy_url,
height: self.height,
width: self.width,
ephemeral: self.ephemeral,
duration_secs: self.duration_secs,
waveform: self.waveform,
content: self.content,
};
(filename, updated_struct)
}
/// Moves `self.content_type` out of `self` and returns it.
pub fn move_content_type(self) -> (Option<String>, PartialDiscordFileAttachment) {
let content_type = self.content_type;
let updated_struct = PartialDiscordFileAttachment {
id: self.id,
filename: self.filename,
description: self.description,
content_type: None,
size: self.size,
url: self.url,
proxy_url: self.proxy_url,
height: self.height,
width: self.width,
ephemeral: self.ephemeral,
duration_secs: self.duration_secs,
waveform: self.waveform,
content: self.content,
};
(content_type, updated_struct)
}
pub fn set_id(&mut self, id: i16) {
self.id = Some(id);
}
}

View File

@ -1,14 +1,13 @@
use std::sync::{Arc, RwLock};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::gateway::Shared;
use crate::types::utils::Snowflake; use crate::types::utils::Snowflake;
#[derive(Serialize, Deserialize, Debug, Default, Clone)] #[derive(Serialize, Deserialize, Debug, Default, Clone)]
/// See <https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object> /// See <https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object>
pub struct AuditLogEntry { pub struct AuditLogEntry {
pub target_id: Option<String>, pub target_id: Option<String>,
pub changes: Option<Vec<Arc<RwLock<AuditLogChange>>>>, pub changes: Option<Vec<Shared<AuditLogChange>>>,
pub user_id: Option<Snowflake>, pub user_id: Option<Snowflake>,
pub id: Snowflake, pub id: Snowflake,
// to:do implement an enum for these types // to:do implement an enum for these types

View File

@ -1,5 +1,4 @@
use std::sync::{Arc, RwLock}; use crate::gateway::Shared;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use crate::gateway::Updateable; use crate::gateway::Updateable;
@ -21,8 +20,8 @@ pub struct AutoModerationRule {
pub creator_id: Snowflake, pub creator_id: Snowflake,
pub event_type: AutoModerationRuleEventType, pub event_type: AutoModerationRuleEventType,
pub trigger_type: AutoModerationRuleTriggerType, pub trigger_type: AutoModerationRuleTriggerType,
pub trigger_metadata: Arc<RwLock<AutoModerationRuleTriggerMetadata>>, pub trigger_metadata: Shared<AutoModerationRuleTriggerMetadata>,
pub actions: Vec<Arc<RwLock<AutoModerationAction>>>, pub actions: Vec<Shared<AutoModerationAction>>,
pub enabled: bool, pub enabled: bool,
pub exempt_roles: Vec<Snowflake>, pub exempt_roles: Vec<Snowflake>,
pub exempt_channels: Vec<Snowflake>, pub exempt_channels: Vec<Snowflake>,
@ -99,7 +98,7 @@ pub enum AutoModerationRuleKeywordPresetType {
pub struct AutoModerationAction { pub struct AutoModerationAction {
#[serde(rename = "type")] #[serde(rename = "type")]
pub action_type: AutoModerationActionType, pub action_type: AutoModerationActionType,
pub metadata: Option<Arc<RwLock<AutoModerationActionMetadata>>>, pub metadata: Option<Shared<AutoModerationActionMetadata>>,
} }
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)] #[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]

View File

@ -1,11 +1,10 @@
use std::sync::{Arc, RwLock};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_aux::prelude::deserialize_string_from_number; use serde_aux::prelude::deserialize_string_from_number;
use serde_repr::{Deserialize_repr, Serialize_repr}; use serde_repr::{Deserialize_repr, Serialize_repr};
use std::fmt::Debug; use std::fmt::Debug;
use crate::gateway::Shared;
use crate::types::{ use crate::types::{
entities::{GuildMember, User}, entities::{GuildMember, User},
utils::Snowflake, utils::Snowflake,
@ -71,13 +70,13 @@ pub struct Channel {
pub permission_overwrites: Option<sqlx::types::Json<Vec<PermissionOverwrite>>>, pub permission_overwrites: Option<sqlx::types::Json<Vec<PermissionOverwrite>>>,
#[cfg(not(feature = "sqlx"))] #[cfg(not(feature = "sqlx"))]
#[cfg_attr(feature = "client", observe_option_vec)] #[cfg_attr(feature = "client", observe_option_vec)]
pub permission_overwrites: Option<Vec<Arc<RwLock<PermissionOverwrite>>>>, pub permission_overwrites: Option<Vec<Shared<PermissionOverwrite>>>,
pub permissions: Option<String>, pub permissions: Option<String>,
pub position: Option<i32>, pub position: Option<i32>,
pub rate_limit_per_user: Option<i32>, pub rate_limit_per_user: Option<i32>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
#[cfg_attr(feature = "client", observe_option_vec)] #[cfg_attr(feature = "client", observe_option_vec)]
pub recipients: Option<Vec<Arc<RwLock<User>>>>, pub recipients: Option<Vec<Shared<User>>>,
pub rtc_region: Option<String>, pub rtc_region: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub thread_metadata: Option<ThreadMetadata>, pub thread_metadata: Option<ThreadMetadata>,
@ -171,7 +170,7 @@ pub struct ThreadMember {
pub user_id: Option<Snowflake>, pub user_id: Option<Snowflake>,
pub join_timestamp: Option<String>, pub join_timestamp: Option<String>,
pub flags: Option<u64>, pub flags: Option<u64>,
pub member: Option<Arc<RwLock<GuildMember>>>, pub member: Option<Shared<GuildMember>>,
} }
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)] #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]

View File

@ -15,20 +15,29 @@ impl ConfigEntity {
let Some(v) = self.value.as_ref() else { let Some(v) = self.value.as_ref() else {
return None; return None;
}; };
Some(v.as_str().expect("value is not a string").to_string()) let Some(v) = v.as_str() else {
return None;
};
Some(v.to_string())
} }
pub fn as_bool(&self) -> Option<bool> { pub fn as_bool(&self) -> Option<bool> {
let Some(v) = self.value.as_ref() else { let Some(v) = self.value.as_ref() else {
return None; return None;
}; };
Some(v.as_bool().expect("value is not a boolean")) let Some(v) = v.as_bool() else {
return None;
};
Some(v)
} }
pub fn as_int(&self) -> Option<i64> { pub fn as_int(&self) -> Option<i64> {
let Some(v) = self.value.as_ref() else { let Some(v) = self.value.as_ref() else {
return None; return None;
}; };
Some(v.as_i64().expect("value is not a number")) let Some(v) = v.as_i64() else {
return None;
};
Some(v)
} }
} }

View File

@ -1,8 +1,8 @@
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::{Arc, RwLock};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::gateway::Shared;
use crate::types::entities::User; use crate::types::entities::User;
use crate::types::Snowflake; use crate::types::Snowflake;
@ -31,7 +31,7 @@ pub struct Emoji {
#[cfg(not(feature = "sqlx"))] #[cfg(not(feature = "sqlx"))]
pub roles: Option<Vec<Snowflake>>, pub roles: Option<Vec<Snowflake>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub user: Option<Arc<RwLock<User>>>, pub user: Option<Shared<User>>,
pub require_colons: Option<bool>, pub require_colons: Option<bool>,
pub managed: Option<bool>, pub managed: Option<bool>,
pub animated: Option<bool>, pub animated: Option<bool>,
@ -62,37 +62,3 @@ impl PartialEq for Emoji {
|| self.available != other.available) || self.available != other.available)
} }
} }
impl PartialOrd for Emoji {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
match self.id.partial_cmp(&other.id) {
Some(core::cmp::Ordering::Equal) => {}
ord => return ord,
}
match self.name.partial_cmp(&other.name) {
Some(core::cmp::Ordering::Equal) => {}
ord => return ord,
}
match self.roles.partial_cmp(&other.roles) {
Some(core::cmp::Ordering::Equal) => {}
ord => return ord,
}
match self.roles.partial_cmp(&other.roles) {
Some(core::cmp::Ordering::Equal) => {}
ord => return ord,
}
match self.require_colons.partial_cmp(&other.require_colons) {
Some(core::cmp::Ordering::Equal) => {}
ord => return ord,
}
match self.managed.partial_cmp(&other.managed) {
Some(core::cmp::Ordering::Equal) => {}
ord => return ord,
}
match self.animated.partial_cmp(&other.animated) {
Some(core::cmp::Ordering::Equal) => {}
ord => return ord,
}
self.available.partial_cmp(&other.available)
}
}

View File

@ -1,11 +1,11 @@
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::{Arc, RwLock};
use bitflags::bitflags; use bitflags::bitflags;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr}; use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::gateway::Shared;
use crate::types::types::guild_configuration::GuildFeaturesList; use crate::types::types::guild_configuration::GuildFeaturesList;
use crate::types::{ use crate::types::{
entities::{Channel, Emoji, RoleObject, Sticker, User, VoiceState, Webhook}, entities::{Channel, Emoji, RoleObject, Sticker, User, VoiceState, Webhook},
@ -45,14 +45,14 @@ pub struct Guild {
pub bans: Option<Vec<GuildBan>>, pub bans: Option<Vec<GuildBan>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
#[cfg_attr(feature = "client", observe_option_vec)] #[cfg_attr(feature = "client", observe_option_vec)]
pub channels: Option<Vec<Arc<RwLock<Channel>>>>, pub channels: Option<Vec<Shared<Channel>>>,
pub default_message_notifications: Option<MessageNotificationLevel>, pub default_message_notifications: Option<MessageNotificationLevel>,
pub description: Option<String>, pub description: Option<String>,
pub discovery_splash: Option<String>, pub discovery_splash: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
#[cfg_attr(feature = "client", observe_vec)] #[cfg_attr(feature = "client", observe_vec)]
#[serde(default)] #[serde(default)]
pub emojis: Vec<Arc<RwLock<Emoji>>>, pub emojis: Vec<Shared<Emoji>>,
pub explicit_content_filter: Option<i32>, pub explicit_content_filter: Option<i32>,
//#[cfg_attr(feature = "sqlx", sqlx(try_from = "String"))] //#[cfg_attr(feature = "sqlx", sqlx(try_from = "String"))]
pub features: Option<GuildFeaturesList>, pub features: Option<GuildFeaturesList>,
@ -88,7 +88,7 @@ pub struct Guild {
pub region: Option<String>, pub region: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
#[cfg_attr(feature = "client", observe_option_vec)] #[cfg_attr(feature = "client", observe_option_vec)]
pub roles: Option<Vec<Arc<RwLock<RoleObject>>>>, pub roles: Option<Vec<Shared<RoleObject>>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub rules_channel: Option<String>, pub rules_channel: Option<String>,
pub rules_channel_id: Option<Snowflake>, pub rules_channel_id: Option<Snowflake>,
@ -102,10 +102,10 @@ pub struct Guild {
pub verification_level: Option<VerificationLevel>, pub verification_level: Option<VerificationLevel>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
#[cfg_attr(feature = "client", observe_option_vec)] #[cfg_attr(feature = "client", observe_option_vec)]
pub voice_states: Option<Vec<Arc<RwLock<VoiceState>>>>, pub voice_states: Option<Vec<Shared<VoiceState>>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
#[cfg_attr(feature = "client", observe_option_vec)] #[cfg_attr(feature = "client", observe_option_vec)]
pub webhooks: Option<Vec<Arc<RwLock<Webhook>>>>, pub webhooks: Option<Vec<Shared<Webhook>>>,
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]
pub welcome_screen: Option<sqlx::types::Json<WelcomeScreenObject>>, pub welcome_screen: Option<sqlx::types::Json<WelcomeScreenObject>>,
#[cfg(not(feature = "sqlx"))] #[cfg(not(feature = "sqlx"))]
@ -217,8 +217,6 @@ impl std::cmp::PartialEq for Guild {
} }
} }
impl std::cmp::Eq for Guild {}
/// See <https://docs.spacebar.chat/routes/#get-/guilds/-guild_id-/bans/-user-> /// See <https://docs.spacebar.chat/routes/#get-/guilds/-guild_id-/bans/-user->
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Hash)] #[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
@ -239,11 +237,11 @@ pub struct GuildInvite {
pub created_at: DateTime<Utc>, pub created_at: DateTime<Utc>,
pub expires_at: Option<DateTime<Utc>>, pub expires_at: Option<DateTime<Utc>>,
pub guild_id: Snowflake, pub guild_id: Snowflake,
pub guild: Option<Arc<RwLock<Guild>>>, pub guild: Option<Shared<Guild>>,
pub channel_id: Snowflake, pub channel_id: Snowflake,
pub channel: Option<Arc<RwLock<Channel>>>, pub channel: Option<Shared<Channel>>,
pub inviter_id: Option<Snowflake>, pub inviter_id: Option<Snowflake>,
pub inviter: Option<Arc<RwLock<User>>>, pub inviter: Option<Shared<User>>,
pub target_user_id: Option<Snowflake>, pub target_user_id: Option<Snowflake>,
pub target_user: Option<String>, pub target_user: Option<String>,
pub target_user_type: Option<i32>, pub target_user_type: Option<i32>,
@ -296,7 +294,7 @@ pub struct GuildScheduledEvent {
pub entity_type: GuildScheduledEventEntityType, pub entity_type: GuildScheduledEventEntityType,
pub entity_id: Option<Snowflake>, pub entity_id: Option<Snowflake>,
pub entity_metadata: Option<GuildScheduledEventEntityMetadata>, pub entity_metadata: Option<GuildScheduledEventEntityMetadata>,
pub creator: Option<Arc<RwLock<User>>>, pub creator: Option<Shared<User>>,
pub user_count: Option<u64>, pub user_count: Option<u64>,
pub image: Option<String>, pub image: Option<String>,
} }

View File

@ -1,7 +1,6 @@
use std::sync::{Arc, RwLock};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::gateway::Shared;
use crate::types::{entities::PublicUser, Snowflake}; use crate::types::{entities::PublicUser, Snowflake};
#[derive(Debug, Deserialize, Default, Serialize, Clone)] #[derive(Debug, Deserialize, Default, Serialize, Clone)]
@ -10,7 +9,7 @@ use crate::types::{entities::PublicUser, Snowflake};
/// # Reference /// # Reference
/// See <https://discord-userdoccers.vercel.app/resources/guild#guild-member-object> /// See <https://discord-userdoccers.vercel.app/resources/guild#guild-member-object>
pub struct GuildMember { pub struct GuildMember {
pub user: Option<Arc<RwLock<PublicUser>>>, pub user: Option<Shared<PublicUser>>,
pub nick: Option<String>, pub nick: Option<String>,
pub avatar: Option<String>, pub avatar: Option<String>,
pub roles: Vec<Snowflake>, pub roles: Vec<Snowflake>,

View File

@ -1,8 +1,7 @@
use std::sync::{Arc, RwLock};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::gateway::Shared;
use crate::types::{ use crate::types::{
entities::{Application, User}, entities::{Application, User},
utils::Snowflake, utils::Snowflake,
@ -23,14 +22,14 @@ pub struct Integration {
pub expire_behaviour: Option<u8>, pub expire_behaviour: Option<u8>,
pub expire_grace_period: Option<u16>, pub expire_grace_period: Option<u16>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub user: Option<Arc<RwLock<User>>>, pub user: Option<Shared<User>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub account: IntegrationAccount, pub account: IntegrationAccount,
pub synced_at: Option<DateTime<Utc>>, pub synced_at: Option<DateTime<Utc>>,
pub subscriber_count: Option<f64>, pub subscriber_count: Option<f64>,
pub revoked: Option<bool>, pub revoked: Option<bool>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub application: Option<Arc<RwLock<Application>>>, pub application: Option<Shared<Application>>,
pub scopes: Option<Vec<String>>, pub scopes: Option<Vec<String>>,
} }

View File

@ -1,8 +1,7 @@
use std::sync::{Arc, RwLock};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::gateway::Shared;
use crate::types::{Snowflake, WelcomeScreenObject}; use crate::types::{Snowflake, WelcomeScreenObject};
use super::guild::GuildScheduledEvent; use super::guild::GuildScheduledEvent;
@ -21,7 +20,7 @@ pub struct Invite {
pub flags: Option<i32>, pub flags: Option<i32>,
pub guild: Option<InviteGuild>, pub guild: Option<InviteGuild>,
pub guild_id: Option<Snowflake>, pub guild_id: Option<Snowflake>,
pub guild_scheduled_event: Option<Arc<RwLock<GuildScheduledEvent>>>, pub guild_scheduled_event: Option<Shared<GuildScheduledEvent>>,
#[serde(rename = "type")] #[serde(rename = "type")]
pub invite_type: Option<i32>, pub invite_type: Option<i32>,
pub inviter: Option<User>, pub inviter: Option<User>,
@ -59,7 +58,7 @@ pub struct InviteGuild {
/// See <https://discord-userdoccers.vercel.app/resources/invite#invite-stage-instance-object> /// See <https://discord-userdoccers.vercel.app/resources/invite#invite-stage-instance-object>
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct InviteStageInstance { pub struct InviteStageInstance {
pub members: Vec<Arc<RwLock<GuildMember>>>, pub members: Vec<Shared<GuildMember>>,
pub participant_count: i32, pub participant_count: i32,
pub speaker_count: i32, pub speaker_count: i32,
pub topic: String, pub topic: String,

View File

@ -1,7 +1,6 @@
use std::sync::{Arc, RwLock};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::gateway::Shared;
use crate::types::{ use crate::types::{
entities::{ entities::{
Application, Attachment, Channel, Emoji, GuildMember, PublicUser, RoleSubscriptionData, Application, Attachment, Channel, Emoji, GuildMember, PublicUser, RoleSubscriptionData,
@ -121,7 +120,7 @@ pub struct MessageInteraction {
pub interaction_type: u8, pub interaction_type: u8,
pub name: String, pub name: String,
pub user: User, pub user: User,
pub member: Option<Arc<RwLock<GuildMember>>>, pub member: Option<Shared<GuildMember>>,
} }
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize, Eq, PartialOrd, Ord)] #[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize, Eq, PartialOrd, Ord)]
@ -219,7 +218,7 @@ pub struct EmbedField {
inline: Option<bool>, inline: Option<bool>,
} }
#[derive(Debug, Clone, Serialize, Deserialize, PartialOrd, PartialEq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Reaction { pub struct Reaction {
pub count: u32, pub count: u32,
pub burst_count: u32, pub burst_count: u32,

View File

@ -23,6 +23,7 @@ pub use user_settings::*;
pub use voice_state::*; pub use voice_state::*;
pub use webhook::*; pub use webhook::*;
use crate::gateway::Shared;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use crate::gateway::Updateable; use crate::gateway::Updateable;
@ -34,7 +35,6 @@ use async_trait::async_trait;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use std::fmt::Debug; use std::fmt::Debug;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
@ -69,9 +69,9 @@ pub trait Composite<T: Updateable + Clone + Debug> {
async fn watch_whole(self, gateway: &GatewayHandle) -> Self; async fn watch_whole(self, gateway: &GatewayHandle) -> Self;
async fn option_observe_fn( async fn option_observe_fn(
value: Option<Arc<RwLock<T>>>, value: Option<Shared<T>>,
gateway: &GatewayHandle, gateway: &GatewayHandle,
) -> Option<Arc<RwLock<T>>> ) -> Option<Shared<T>>
where where
T: Composite<T> + Debug, T: Composite<T> + Debug,
{ {
@ -84,9 +84,9 @@ pub trait Composite<T: Updateable + Clone + Debug> {
} }
async fn option_vec_observe_fn( async fn option_vec_observe_fn(
value: Option<Vec<Arc<RwLock<T>>>>, value: Option<Vec<Shared<T>>>,
gateway: &GatewayHandle, gateway: &GatewayHandle,
) -> Option<Vec<Arc<RwLock<T>>>> ) -> Option<Vec<Shared<T>>>
where where
T: Composite<T>, T: Composite<T>,
{ {
@ -101,17 +101,14 @@ pub trait Composite<T: Updateable + Clone + Debug> {
} }
} }
async fn value_observe_fn(value: Arc<RwLock<T>>, gateway: &GatewayHandle) -> Arc<RwLock<T>> async fn value_observe_fn(value: Shared<T>, gateway: &GatewayHandle) -> Shared<T>
where where
T: Composite<T>, T: Composite<T>,
{ {
gateway.observe(value).await gateway.observe(value).await
} }
async fn vec_observe_fn( async fn vec_observe_fn(value: Vec<Shared<T>>, gateway: &GatewayHandle) -> Vec<Shared<T>>
value: Vec<Arc<RwLock<T>>>,
gateway: &GatewayHandle,
) -> Vec<Arc<RwLock<T>>>
where where
T: Composite<T>, T: Composite<T>,
{ {
@ -122,3 +119,19 @@ pub trait Composite<T: Updateable + Clone + Debug> {
vec vec
} }
} }
pub trait IntoShared {
/// Uses [`Shared`] to provide an ergonomic alternative to `Arc::new(RwLock::new(obj))`.
///
/// [`Shared<Self>`] can then be observed using the [`Gateway`], turning the underlying
/// `dyn Composite<Self>` 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<Self>;
}
impl<T: Sized> IntoShared for T {
fn into_shared(self) -> Shared<Self> {
Arc::new(RwLock::new(self))
}
}

View File

@ -1,9 +1,8 @@
use std::sync::{Arc, RwLock};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr}; use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::gateway::Shared;
use crate::types::Snowflake; use crate::types::Snowflake;
use super::PublicUser; use super::PublicUser;
@ -15,7 +14,7 @@ pub struct Relationship {
#[serde(rename = "type")] #[serde(rename = "type")]
pub relationship_type: RelationshipType, pub relationship_type: RelationshipType,
pub nickname: Option<String>, pub nickname: Option<String>,
pub user: Arc<RwLock<PublicUser>>, pub user: Shared<PublicUser>,
pub since: Option<DateTime<Utc>>, pub since: Option<DateTime<Utc>>,
} }

View File

@ -1,7 +1,6 @@
use std::sync::{Arc, RwLock};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::gateway::Shared;
use crate::types::{entities::User, utils::Snowflake}; use crate::types::{entities::User, utils::Snowflake};
#[derive(Debug, Serialize, Deserialize, Clone, Default)] #[derive(Debug, Serialize, Deserialize, Clone, Default)]
@ -24,7 +23,7 @@ pub struct Sticker {
pub available: Option<bool>, pub available: Option<bool>,
pub guild_id: Option<Snowflake>, pub guild_id: Option<Snowflake>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub user: Option<Arc<RwLock<User>>>, pub user: Option<Shared<User>>,
pub sort_value: Option<u8>, pub sort_value: Option<u8>,
} }

View File

@ -1,7 +1,6 @@
use std::sync::{Arc, RwLock};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::gateway::Shared;
use crate::types::entities::User; use crate::types::entities::User;
use crate::types::Snowflake; use crate::types::Snowflake;
@ -21,5 +20,5 @@ pub struct TeamMember {
pub membership_state: u8, pub membership_state: u8,
pub permissions: Vec<String>, pub permissions: Vec<String>,
pub team_id: Snowflake, pub team_id: Snowflake,
pub user: Arc<RwLock<User>>, pub user: Shared<User>,
} }

View File

@ -1,8 +1,7 @@
use std::sync::{Arc, RwLock};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::gateway::Shared;
use crate::types::{ use crate::types::{
entities::{Guild, User}, entities::{Guild, User},
utils::Snowflake, utils::Snowflake,
@ -18,13 +17,13 @@ pub struct GuildTemplate {
pub usage_count: Option<u64>, pub usage_count: Option<u64>,
pub creator_id: Snowflake, pub creator_id: Snowflake,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub creator: Arc<RwLock<User>>, pub creator: Shared<User>,
pub created_at: DateTime<Utc>, pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>, pub updated_at: DateTime<Utc>,
pub source_guild_id: Snowflake, pub source_guild_id: Snowflake,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub source_guild: Vec<Arc<RwLock<Guild>>>, pub source_guild: Vec<Shared<Guild>>,
// Unsure how a {recursive: Guild} looks like, might be a Vec? // Unsure how a {recursive: Guild} looks like, might be a Vec?
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub serialized_source_guild: Vec<Arc<RwLock<Guild>>>, pub serialized_source_guild: Vec<Shared<Guild>>,
} }

View File

@ -26,7 +26,7 @@ pub struct UserData {
} }
impl User { impl User {
pub fn to_public_user(self) -> PublicUser { pub fn into_public_user(self) -> PublicUser {
PublicUser::from(self) PublicUser::from(self)
} }
} }
@ -133,7 +133,7 @@ bitflags::bitflags! {
} }
} }
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)] #[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
pub struct UserProfileMetadata { pub struct UserProfileMetadata {
pub guild_id: Option<Snowflake>, pub guild_id: Option<Snowflake>,
pub pronouns: String, pub pronouns: String,

View File

@ -3,6 +3,8 @@ use std::sync::{Arc, RwLock};
use chrono::{serde::ts_milliseconds_option, Utc}; use chrono::{serde::ts_milliseconds_option, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::gateway::Shared;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))] #[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
@ -77,7 +79,7 @@ pub struct UserSettings {
#[cfg(not(feature = "sqlx"))] #[cfg(not(feature = "sqlx"))]
pub restricted_guilds: Vec<String>, pub restricted_guilds: Vec<String>,
pub show_current_game: bool, pub show_current_game: bool,
pub status: Arc<RwLock<UserStatus>>, pub status: Shared<UserStatus>,
pub stream_notifications_enabled: bool, pub stream_notifications_enabled: bool,
pub theme: UserTheme, pub theme: UserTheme,
pub timezone_offset: i16, pub timezone_offset: i16,
@ -153,5 +155,5 @@ pub struct GuildFolder {
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct LoginResult { pub struct LoginResult {
pub token: String, pub token: String,
pub settings: Arc<RwLock<UserSettings>>, pub settings: Shared<UserSettings>,
} }

View File

@ -1,8 +1,7 @@
use std::sync::{Arc, RwLock};
#[cfg(feature = "client")] #[cfg(feature = "client")]
use chorus_macros::Composite; use chorus_macros::Composite;
use crate::gateway::Shared;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use crate::types::Composite; use crate::types::Composite;
@ -33,7 +32,7 @@ pub struct VoiceState {
pub guild: Option<Guild>, pub guild: Option<Guild>,
pub channel_id: Option<Snowflake>, pub channel_id: Option<Snowflake>,
pub user_id: Snowflake, pub user_id: Snowflake,
pub member: Option<Arc<RwLock<GuildMember>>>, pub member: Option<Shared<GuildMember>>,
/// Includes alphanumeric characters, not a snowflake /// Includes alphanumeric characters, not a snowflake
pub session_id: String, pub session_id: String,
pub token: Option<String>, pub token: Option<String>,
@ -49,6 +48,7 @@ pub struct VoiceState {
} }
impl Updateable for VoiceState { impl Updateable for VoiceState {
#[cfg(not(tarpaulin_include))]
fn id(&self) -> Snowflake { fn id(&self) -> Snowflake {
if let Some(id) = self.id { if let Some(id) = self.id {
id // ID exists: Only the case for Spacebar Server impls id // ID exists: Only the case for Spacebar Server impls

View File

@ -1,8 +1,8 @@
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::{Arc, RwLock};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::gateway::Shared;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use crate::gateway::Updateable; use crate::gateway::Updateable;
@ -36,10 +36,10 @@ pub struct Webhook {
pub application_id: Snowflake, pub application_id: Snowflake,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub user: Option<Arc<RwLock<User>>>, pub user: Option<Shared<User>>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub source_guild: Option<Arc<RwLock<Guild>>>, pub source_guild: Option<Shared<Guild>>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>, pub url: Option<String>,
} }

View File

@ -31,7 +31,9 @@ pub struct AutoModerationRuleUpdate {
} }
#[cfg(feature = "client")] #[cfg(feature = "client")]
#[cfg(not(tarpaulin_include))]
impl UpdateMessage<AutoModerationRule> for AutoModerationRuleUpdate { impl UpdateMessage<AutoModerationRule> for AutoModerationRuleUpdate {
#[cfg(not(tarpaulin_include))]
fn id(&self) -> Option<Snowflake> { fn id(&self) -> Option<Snowflake> {
Some(self.rule.id) Some(self.rule.id)
} }

View File

@ -1,4 +1,5 @@
use crate::types::events::WebSocketEvent; use crate::types::events::WebSocketEvent;
use crate::types::IntoShared;
use crate::types::{entities::Channel, JsonField, Snowflake, SourceUrlField}; use crate::types::{entities::Channel, JsonField, Snowflake, SourceUrlField};
use chorus_macros::{JsonField, SourceUrlField}; use chorus_macros::{JsonField, SourceUrlField};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
@ -8,7 +9,7 @@ use serde::{Deserialize, Serialize};
use super::UpdateMessage; use super::UpdateMessage;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use std::sync::{Arc, RwLock}; use crate::gateway::Shared;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use crate::types::Guild; use crate::types::Guild;
@ -38,13 +39,14 @@ impl WebSocketEvent for ChannelCreate {}
#[cfg(feature = "client")] #[cfg(feature = "client")]
impl UpdateMessage<Guild> for ChannelCreate { impl UpdateMessage<Guild> for ChannelCreate {
#[cfg(not(tarpaulin_include))]
fn id(&self) -> Option<Snowflake> { fn id(&self) -> Option<Snowflake> {
self.channel.guild_id self.channel.guild_id
} }
fn update(&mut self, object_to_update: Arc<RwLock<Guild>>) { fn update(&mut self, object_to_update: Shared<Guild>) {
let mut write = object_to_update.write().unwrap(); let mut write = object_to_update.write().unwrap();
let update = Arc::new(RwLock::new(self.channel.clone())); let update = self.channel.clone().into_shared();
if write.channels.is_some() { if write.channels.is_some() {
write.channels.as_mut().unwrap().push(update); write.channels.as_mut().unwrap().push(update);
} else { } else {
@ -68,10 +70,12 @@ impl WebSocketEvent for ChannelUpdate {}
#[cfg(feature = "client")] #[cfg(feature = "client")]
impl UpdateMessage<Channel> for ChannelUpdate { impl UpdateMessage<Channel> for ChannelUpdate {
fn update(&mut self, object_to_update: Arc<RwLock<Channel>>) { fn update(&mut self, object_to_update: Shared<Channel>) {
let mut write = object_to_update.write().unwrap(); let mut write = object_to_update.write().unwrap();
*write = self.channel.clone(); *write = self.channel.clone();
} }
#[cfg(not(tarpaulin_include))]
fn id(&self) -> Option<Snowflake> { fn id(&self) -> Option<Snowflake> {
Some(self.channel.id) Some(self.channel.id)
} }
@ -110,11 +114,12 @@ pub struct ChannelDelete {
#[cfg(feature = "client")] #[cfg(feature = "client")]
impl UpdateMessage<Guild> for ChannelDelete { impl UpdateMessage<Guild> for ChannelDelete {
#[cfg(not(tarpaulin_include))]
fn id(&self) -> Option<Snowflake> { fn id(&self) -> Option<Snowflake> {
self.channel.guild_id self.channel.guild_id
} }
fn update(&mut self, object_to_update: Arc<RwLock<Guild>>) { fn update(&mut self, object_to_update: Shared<Guild>) {
if self.id().is_none() { if self.id().is_none() {
return; return;
} }

View File

@ -5,8 +5,8 @@ use serde::{Deserialize, Serialize};
use crate::types::entities::{Guild, PublicUser, UnavailableGuild}; use crate::types::entities::{Guild, PublicUser, UnavailableGuild};
use crate::types::events::WebSocketEvent; use crate::types::events::WebSocketEvent;
use crate::types::{ use crate::types::{
AuditLogEntry, Emoji, GuildMember, GuildScheduledEvent, JsonField, RoleObject, Snowflake, AuditLogEntry, Emoji, GuildMember, GuildScheduledEvent, IntoShared, JsonField, RoleObject,
SourceUrlField, Sticker, Snowflake, SourceUrlField, Sticker,
}; };
use super::PresenceUpdate; use super::PresenceUpdate;
@ -14,7 +14,7 @@ use super::PresenceUpdate;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use super::UpdateMessage; use super::UpdateMessage;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use std::sync::{Arc, RwLock}; use crate::gateway::Shared;
#[derive(Debug, Deserialize, Serialize, Default, Clone, SourceUrlField, JsonField)] #[derive(Debug, Deserialize, Serialize, Default, Clone, SourceUrlField, JsonField)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-create>; /// See <https://discord.com/developers/docs/topics/gateway-events#guild-create>;
@ -30,7 +30,9 @@ pub struct GuildCreate {
} }
#[cfg(feature = "client")] #[cfg(feature = "client")]
#[cfg(not(tarpaulin_include))]
impl UpdateMessage<Guild> for GuildCreate { impl UpdateMessage<Guild> for GuildCreate {
#[cfg(not(tarpaulin_include))]
fn id(&self) -> Option<Snowflake> { fn id(&self) -> Option<Snowflake> {
match &self.d { match &self.d {
GuildCreateDataOption::UnavailableGuild(unavailable) => Some(unavailable.id), GuildCreateDataOption::UnavailableGuild(unavailable) => Some(unavailable.id),
@ -38,7 +40,7 @@ impl UpdateMessage<Guild> for GuildCreate {
} }
} }
fn update(&mut self, _: Arc<RwLock<Guild>>) {} fn update(&mut self, _: Shared<Guild>) {}
} }
#[derive(Debug, Deserialize, Serialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]
@ -92,6 +94,7 @@ impl WebSocketEvent for GuildUpdate {}
#[cfg(feature = "client")] #[cfg(feature = "client")]
impl UpdateMessage<Guild> for GuildUpdate { impl UpdateMessage<Guild> for GuildUpdate {
#[cfg(not(tarpaulin_include))]
fn id(&self) -> Option<Snowflake> { fn id(&self) -> Option<Snowflake> {
Some(self.guild.id) Some(self.guild.id)
} }
@ -111,10 +114,11 @@ pub struct GuildDelete {
#[cfg(feature = "client")] #[cfg(feature = "client")]
impl UpdateMessage<Guild> for GuildDelete { impl UpdateMessage<Guild> for GuildDelete {
#[cfg(not(tarpaulin_include))]
fn id(&self) -> Option<Snowflake> { fn id(&self) -> Option<Snowflake> {
Some(self.guild.id) Some(self.guild.id)
} }
fn update(&mut self, _: Arc<RwLock<Guild>>) {} fn update(&mut self, _: Shared<Guild>) {}
} }
impl WebSocketEvent for GuildDelete {} impl WebSocketEvent for GuildDelete {}
@ -225,20 +229,21 @@ impl WebSocketEvent for GuildRoleCreate {}
#[cfg(feature = "client")] #[cfg(feature = "client")]
impl UpdateMessage<Guild> for GuildRoleCreate { impl UpdateMessage<Guild> for GuildRoleCreate {
#[cfg(not(tarpaulin_include))]
fn id(&self) -> Option<Snowflake> { fn id(&self) -> Option<Snowflake> {
Some(self.guild_id) Some(self.guild_id)
} }
fn update(&mut self, object_to_update: Arc<RwLock<Guild>>) { fn update(&mut self, object_to_update: Shared<Guild>) {
let mut object_to_update = object_to_update.write().unwrap(); let mut object_to_update = object_to_update.write().unwrap();
if object_to_update.roles.is_some() { if object_to_update.roles.is_some() {
object_to_update object_to_update
.roles .roles
.as_mut() .as_mut()
.unwrap() .unwrap()
.push(Arc::new(RwLock::new(self.role.clone()))); .push(self.role.clone().into_shared());
} else { } else {
object_to_update.roles = Some(Vec::from([Arc::new(RwLock::new(self.role.clone()))])); object_to_update.roles = Some(Vec::from([self.role.clone().into_shared()]));
} }
} }
} }
@ -258,11 +263,12 @@ impl WebSocketEvent for GuildRoleUpdate {}
#[cfg(feature = "client")] #[cfg(feature = "client")]
impl UpdateMessage<RoleObject> for GuildRoleUpdate { impl UpdateMessage<RoleObject> for GuildRoleUpdate {
#[cfg(not(tarpaulin_include))]
fn id(&self) -> Option<Snowflake> { fn id(&self) -> Option<Snowflake> {
Some(self.role.id) Some(self.role.id)
} }
fn update(&mut self, object_to_update: Arc<RwLock<RoleObject>>) { fn update(&mut self, object_to_update: Shared<RoleObject>) {
let mut write = object_to_update.write().unwrap(); let mut write = object_to_update.write().unwrap();
*write = self.role.clone(); *write = self.role.clone();
} }

View File

@ -39,9 +39,9 @@ use serde_json::{from_str, from_value, to_value, Value};
#[cfg(feature = "client")] #[cfg(feature = "client")]
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Debug;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use std::sync::{Arc, RwLock}; use crate::gateway::Shared;
use std::fmt::Debug;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
@ -132,9 +132,10 @@ pub(crate) trait UpdateMessage<T>: Clone + JsonField + SourceUrlField
where where
T: Updateable + Serialize + DeserializeOwned + Clone, T: Updateable + Serialize + DeserializeOwned + Clone,
{ {
fn update(&mut self, object_to_update: Arc<RwLock<T>>) { fn update(&mut self, object_to_update: Shared<T>) {
update_object(self.get_json(), object_to_update) update_object(self.get_json(), object_to_update)
} }
#[cfg(not(tarpaulin_include))]
fn id(&self) -> Option<Snowflake>; fn id(&self) -> Option<Snowflake>;
} }
@ -152,7 +153,7 @@ pub trait SourceUrlField: Clone {
/// Only applicable for events where the Update struct is the same as the Entity struct /// Only applicable for events where the Update struct is the same as the Entity struct
pub(crate) fn update_object( pub(crate) fn update_object(
value: String, value: String,
object: Arc<RwLock<(impl Updateable + Serialize + DeserializeOwned + Clone)>>, object: Shared<(impl Updateable + Serialize + DeserializeOwned + Clone)>,
) { ) {
let data_from_event: HashMap<String, Value> = from_str(&value).unwrap(); let data_from_event: HashMap<String, Value> = from_str(&value).unwrap();
let mut original_data: HashMap<String, Value> = let mut original_data: HashMap<String, Value> =

View File

@ -32,6 +32,7 @@ impl WebSocketEvent for ThreadUpdate {}
#[cfg(feature = "client")] #[cfg(feature = "client")]
impl UpdateMessage<Channel> for ThreadUpdate { impl UpdateMessage<Channel> for ThreadUpdate {
#[cfg(not(tarpaulin_include))]
fn id(&self) -> Option<Snowflake> { fn id(&self) -> Option<Snowflake> {
Some(self.thread.id) Some(self.thread.id)
} }

View File

@ -78,7 +78,7 @@ impl std::default::Default for GetUserGuildSchema {
} }
} }
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, PartialOrd)] #[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq)]
pub struct GuildPreview { pub struct GuildPreview {
pub id: Snowflake, pub id: Snowflake,
pub name: String, pub name: String,

View File

@ -111,7 +111,8 @@ impl Observer<VoiceReady> for VoiceHandler {
*self.voice_udp_connection.lock().await = Some(udp_handle.clone()); *self.voice_udp_connection.lock().await = Some(udp_handle.clone());
let string_ip_address = String::from_utf8(ip_discovery.address).expect("Ip discovery gave non string ip"); let string_ip_address =
String::from_utf8(ip_discovery.address).expect("Ip discovery gave non string ip");
self.voice_gateway_connection self.voice_gateway_connection
.lock() .lock()

View File

@ -1,6 +1,5 @@
use std::sync::{Arc, RwLock}; use chorus::gateway::{Gateway, Shared};
use chorus::types::IntoShared;
use chorus::gateway::Gateway;
use chorus::{ use chorus::{
instance::{ChorusUser, Instance}, instance::{ChorusUser, Instance},
types::{ types::{
@ -16,9 +15,9 @@ pub(crate) struct TestBundle {
pub urls: UrlBundle, pub urls: UrlBundle,
pub user: ChorusUser, pub user: ChorusUser,
pub instance: Instance, pub instance: Instance,
pub guild: Arc<RwLock<Guild>>, pub guild: Shared<Guild>,
pub role: Arc<RwLock<RoleObject>>, pub role: Shared<RoleObject>,
pub channel: Arc<RwLock<Channel>>, pub channel: Shared<Channel>,
} }
#[allow(unused)] #[allow(unused)]
@ -119,9 +118,9 @@ pub(crate) async fn setup() -> TestBundle {
urls, urls,
user, user,
instance, instance,
guild: Arc::new(RwLock::new(guild)), guild: guild.into_shared(),
role: Arc::new(RwLock::new(role)), role: role.into_shared(),
channel: Arc::new(RwLock::new(channel)), channel: channel.into_shared(),
} }
} }

View File

@ -1,12 +1,15 @@
mod common; mod common;
use std::sync::{Arc, RwLock}; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use async_trait::async_trait; use async_trait::async_trait;
use chorus::errors::GatewayError; use chorus::errors::GatewayError;
use chorus::gateway::*; use chorus::gateway::*;
use chorus::types::{self, ChannelModifySchema, GatewayReady, RoleCreateModifySchema, RoleObject}; use chorus::types::{
self, Channel, ChannelCreateSchema, ChannelModifySchema, GatewayReady, IntoShared,
RoleCreateModifySchema, RoleObject,
};
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::*; use wasm_bindgen_test::*;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
@ -82,7 +85,10 @@ async fn test_gateway_authenticate() {
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
#[cfg_attr(not(target_arch = "wasm32"), tokio::test)] #[cfg_attr(not(target_arch = "wasm32"), tokio::test)]
async fn test_self_updating_structs() { async fn test_self_updating_structs() {
// PRETTYFYME: This test is a bit of a mess, but it works. Ideally, each self-updating struct
// would have its own test.
let mut bundle = common::setup().await; let mut bundle = common::setup().await;
let received_channel = bundle let received_channel = bundle
.user .user
.gateway .gateway
@ -110,6 +116,34 @@ async fn test_self_updating_structs() {
"selfupdating".to_string() "selfupdating".to_string()
); );
let guild = bundle
.user
.gateway
.observe_and_into_inner(bundle.guild.clone())
.await;
assert!(guild.channels.is_none());
Channel::create(
&mut bundle.user,
guild.id,
None,
ChannelCreateSchema {
name: "selfupdating2".to_string(),
channel_type: Some(types::ChannelType::GuildText),
..Default::default()
},
)
.await
.unwrap();
let guild = bundle
.user
.gateway
.observe_and_into_inner(guild.into_shared())
.await;
assert!(guild.channels.is_some());
assert!(guild.channels.as_ref().unwrap().len() == 1);
common::teardown(bundle).await common::teardown(bundle).await
} }
@ -144,7 +178,7 @@ async fn test_recursive_self_updating_structs() {
bundle bundle
.user .user
.gateway .gateway
.observe(Arc::new(RwLock::new(role.clone()))) .observe(role.clone().into_shared())
.await; .await;
// Update Guild and check for Guild // Update Guild and check for Guild
let inner_guild = guild.read().unwrap().clone(); let inner_guild = guild.read().unwrap().clone();
@ -157,7 +191,7 @@ async fn test_recursive_self_updating_structs() {
let role_inner = bundle let role_inner = bundle
.user .user
.gateway .gateway
.observe_and_into_inner(Arc::new(RwLock::new(role.clone()))) .observe_and_into_inner(role.clone().into_shared())
.await; .await;
assert_eq!(role_inner.name, "yippieee"); assert_eq!(role_inner.name, "yippieee");
// Check if the change propagated // Check if the change propagated

26
tests/ratelimit.rs Normal file
View File

@ -0,0 +1,26 @@
use chorus::ratelimiter::ChorusRequest;
mod common;
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
#[cfg_attr(not(target_arch = "wasm32"), tokio::test)]
async fn get_limit_config() {
let conf = ChorusRequest::get_limits_config("http://localhost:3001/api")
.await
.unwrap();
assert!(conf.channel.max_pins > 0);
assert!(conf.channel.max_topic > 0);
assert!(conf.channel.max_webhooks > 0);
assert!(conf.guild.max_roles > 0);
assert!(conf.guild.max_channels > 0);
assert!(conf.guild.max_emojis > 0);
assert!(conf.guild.max_channels_in_category > 0);
assert!(conf.guild.max_members > 0);
assert!(conf.message.max_attachment_size > 0);
assert!(conf.message.max_bulk_delete > 0);
assert!(conf.message.max_reactions > 0);
assert!(conf.message.max_characters > 0);
assert!(conf.message.max_tts_characters == 0);
assert!(conf.user.max_guilds > 0);
assert!(conf.user.max_friends > 0);
}

1054
tests/types.rs Normal file

File diff suppressed because it is too large Load Diff

18
tests/user.rs Normal file
View File

@ -0,0 +1,18 @@
use chorus::types::{PublicUser, Snowflake, User};
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
#[cfg_attr(not(target_arch = "wasm32"), test)]
fn to_public_user() {
let mut user = User::default();
let mut public_user = PublicUser {
username: Some("".to_string()),
discriminator: Some("".to_string()),
..Default::default()
};
let id: Snowflake = 1_u64.into();
user.id = id;
public_user.id = id;
let from_user = user.into_public_user();
assert_eq!(public_user, from_user);
}