diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 398c52f..b192a3c 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -2,7 +2,7 @@ name: Build and Test on: push: - branches: [ "main" ] + branches: [ "main", "dev" ] pull_request: branches: [ "main" ] diff --git a/.github/workflows/rust-clippy.yml b/.github/workflows/rust-clippy.yml index dbbba76..0c3840f 100644 --- a/.github/workflows/rust-clippy.yml +++ b/.github/workflows/rust-clippy.yml @@ -11,7 +11,7 @@ name: rust-clippy analyze on: push: - branches: [ "main", "preserve/*" ] + branches: [ "main", "preserve/*", "dev" ] pull_request: # The branches below must be a subset of the branches above branches: [ "main" ] diff --git a/Cargo.lock b/Cargo.lock index 8c8c26e..df89f42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,9 +31,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" +checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" dependencies = [ "memchr", ] @@ -67,7 +67,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", ] [[package]] @@ -177,7 +177,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chorus" -version = "0.4.0" +version = "0.9.0" dependencies = [ "async-trait", "base64 0.21.3", @@ -211,18 +211,20 @@ dependencies = [ [[package]] name = "chorus-macros" -version = "0.1.0" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a81545a60b926f815517dadbbd40cd502294ae2baea25fa8194d854d607512b0" dependencies = [ "async-trait", "quote", - "syn 2.0.29", + "syn 2.0.31", ] [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "95ed24df0632f708f5f6d8082675bef2596f7084dee3dd55f632290bf35bfe0f" dependencies = [ "android-tzdata", "iana-time-zone", @@ -231,7 +233,7 @@ dependencies = [ "serde", "time 0.1.45", "wasm-bindgen", - "winapi", + "windows-targets", ] [[package]] @@ -345,7 +347,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.29", + "syn 2.0.31", ] [[package]] @@ -356,7 +358,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.29", + "syn 2.0.31", ] [[package]] @@ -439,9 +441,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" dependencies = [ "errno-dragonfly", "libc", @@ -581,7 +583,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", ] [[package]] @@ -686,21 +688,20 @@ dependencies = [ [[package]] name = "hashlink" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312f66718a2d7789ffef4f4b7b213138ed9f1eb3aa1d0d82fc99f88fb3ffd26f" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ "hashbrown 0.14.0", ] [[package]] name = "headers" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" +checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ - "base64 0.13.1", - "bitflags 1.3.2", + "base64 0.21.3", "bytes", "headers-core", "http", @@ -1033,9 +1034,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "mime" @@ -1184,9 +1185,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] @@ -1199,11 +1200,11 @@ checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "openssl" -version = "0.10.56" +version = "0.10.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e" +checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "cfg-if", "foreign-types", "libc", @@ -1220,7 +1221,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", ] [[package]] @@ -1231,9 +1232,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.91" +version = "0.9.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" +checksum = "db7e971c2c2bba161b2d2fdf37080177eff520b3bc044787c7f1f5f9e78d869b" dependencies = [ "cc", "libc", @@ -1311,7 +1312,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", ] [[package]] @@ -1355,9 +1356,9 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "poem" -version = "1.3.57" +version = "1.3.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d92c532a37a9e98c0e9a0411e6852b8acccf9ec07d5e6e450b01cbf947d90b" +checksum = "ebc7ae19f3e791ae8108b08801abb3708d64d3a16490c720e0b81040cae87b5d" dependencies = [ "async-trait", "bytes", @@ -1384,14 +1385,14 @@ dependencies = [ [[package]] name = "poem-derive" -version = "1.3.57" +version = "1.3.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5dd58846a1f582215370384c3090c62c9ef188e9d798ffc67ea90d0a1a8a3b8" +checksum = "2550a0bce7273b278894ef3ccc5a6869e7031b6870042f3cc6826ed9faa980a6" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", ] [[package]] @@ -1469,9 +1470,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" dependencies = [ "aho-corasick", "memchr", @@ -1481,9 +1482,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" dependencies = [ "aho-corasick", "memchr", @@ -1588,9 +1589,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.9" +version = "0.38.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bfe0f2582b4931a45d1fa608f8a8722e8b3c7ac54dd6d5f3b3212791fedef49" +checksum = "c0c3dde1fc030af041adc40e79c0e7fbcf431dd24870053d187d7c66e4b87453" dependencies = [ "bitflags 2.4.0", "errno", @@ -1683,7 +1684,7 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", ] [[package]] @@ -1705,7 +1706,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", ] [[package]] @@ -1734,7 +1735,7 @@ dependencies = [ "serde", "serde_json", "serde_with_macros", - "time 0.3.27", + "time 0.3.28", ] [[package]] @@ -1746,7 +1747,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", ] [[package]] @@ -1799,7 +1800,7 @@ dependencies = [ "num-bigint", "num-traits", "thiserror", - "time 0.3.27", + "time 0.3.28", ] [[package]] @@ -2110,9 +2111,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.29" +version = "2.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398" dependencies = [ "proc-macro2", "quote", @@ -2134,22 +2135,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.47" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.47" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", ] [[package]] @@ -2165,9 +2166,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb39ee79a6d8de55f48f2293a830e040392f1c5f16e336bdd1788cd0aadce07" +checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48" dependencies = [ "deranged", "itoa", @@ -2184,9 +2185,9 @@ checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733d258752e9303d392b94b75230d07b0b9c489350c69b851fc6c065fde3e8f9" +checksum = "1a942f44339478ef67935ab2bbaec2fb0322496cf3cbe84b261e06ac3814c572" dependencies = [ "time-core", ] @@ -2233,7 +2234,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", ] [[package]] @@ -2338,7 +2339,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", ] [[package]] @@ -2447,9 +2448,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna", @@ -2516,7 +2517,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", "wasm-bindgen-shared", ] @@ -2550,7 +2551,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/Cargo.toml b/Cargo.toml index 3d2732b..42a5130 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "chorus" description = "A library for interacting with multiple Spacebar-compatible Instances at once." -version = "0.4.0" +version = "0.9.0" license = "AGPL-3.0" edition = "2021" repository = "https://github.com/polyphony-chat/chorus" @@ -50,7 +50,7 @@ thiserror = "1.0.47" jsonwebtoken = "8.3.0" log = "0.4.20" async-trait = "0.1.73" -chorus-macros = { path = "chorus-macros", version = "0.1.0" } +chorus-macros = "0.2.0" [dev-dependencies] tokio = { version = "1.32.0", features = ["full"] } diff --git a/chorus-macros/Cargo.toml b/chorus-macros/Cargo.toml index 098159f..272d99f 100644 --- a/chorus-macros/Cargo.toml +++ b/chorus-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chorus-macros" -version = "0.1.0" +version = "0.2.0" edition = "2021" license = "AGPL-3.0" description = "Macros for the chorus crate." diff --git a/chorus-macros/src/lib.rs b/chorus-macros/src/lib.rs index f825568..fd68df0 100644 --- a/chorus-macros/src/lib.rs +++ b/chorus-macros/src/lib.rs @@ -37,6 +37,25 @@ pub fn jsonfield_macro_derive(input: TokenStream) -> TokenStream { .into() } +#[proc_macro_derive(SourceUrlField)] +pub fn source_url_macro_derive(input: TokenStream) -> TokenStream { + let ast: syn::DeriveInput = syn::parse(input).unwrap(); + + let name = &ast.ident; + // No need for macro hygiene, we're only using this in chorus + quote! { + impl SourceUrlField for #name { + fn get_source_url(&self) -> String { + self.source_url.clone() + } + fn set_source_url(&mut self, url: String) { + self.source_url = url; + } + } + } + .into() +} + #[proc_macro_attribute] pub fn observe_option(_args: TokenStream, input: TokenStream) -> TokenStream { input diff --git a/src/api/auth/login.rs b/src/api/auth/login.rs index 46951be..272ee04 100644 --- a/src/api/auth/login.rs +++ b/src/api/auth/login.rs @@ -3,24 +3,23 @@ use std::sync::{Arc, RwLock}; use reqwest::Client; use serde_json::to_string; -use crate::api::LimitType; use crate::errors::ChorusResult; use crate::gateway::Gateway; use crate::instance::{ChorusUser, Instance}; use crate::ratelimiter::ChorusRequest; -use crate::types::{GatewayIdentifyPayload, LoginResult, LoginSchema}; +use crate::types::{GatewayIdentifyPayload, LimitType, LoginResult, LoginSchema}; impl Instance { /// Logs into an existing account on the spacebar server. /// /// # Reference /// See - pub async fn login_account(&mut self, login_schema: &LoginSchema) -> ChorusResult { + pub async fn login_account(mut self, login_schema: LoginSchema) -> ChorusResult { let endpoint_url = self.urls.api.clone() + "/auth/login"; let chorus_request = ChorusRequest { request: Client::new() .post(endpoint_url) - .body(to_string(login_schema).unwrap()) + .body(to_string(&login_schema).unwrap()) .header("Content-Type", "application/json"), limit_type: LimitType::AuthLogin, }; @@ -46,7 +45,7 @@ impl Instance { self.clone_limits_if_some(), login_result.settings, Arc::new(RwLock::new(object)), - Arc::new(gateway), + gateway, ); Ok(user) } diff --git a/src/api/auth/mod.rs b/src/api/auth/mod.rs index 663bf5e..3ad4a60 100644 --- a/src/api/auth/mod.rs +++ b/src/api/auth/mod.rs @@ -1,5 +1,41 @@ +use std::sync::{Arc, RwLock}; + pub use login::*; pub use register::*; +use crate::{ + errors::ChorusResult, + gateway::Gateway, + instance::{ChorusUser, Instance}, + types::{GatewayIdentifyPayload, User}, +}; + pub mod login; pub mod register; + +impl Instance { + /// Logs into an existing account on the spacebar server, using only a token. + pub async fn login_with_token(&mut self, token: String) -> ChorusResult { + let object_result = self.get_user(token.clone(), None).await; + if let Err(e) = object_result { + return Result::Err(e); + } + + let user_settings = User::get_settings(&token, &self.urls.api, &mut self.clone()) + .await + .unwrap(); + let mut identify = GatewayIdentifyPayload::common(); + let gateway = Gateway::new(self.urls.wss.clone()).await.unwrap(); + identify.token = token.clone(); + gateway.send_identify(identify).await; + let user = ChorusUser::new( + Arc::new(RwLock::new(self.clone())), + token.clone(), + self.clone_limits_if_some(), + Arc::new(RwLock::new(user_settings)), + Arc::new(RwLock::new(object_result.unwrap())), + gateway, + ); + Ok(user) + } +} diff --git a/src/api/auth/register.rs b/src/api/auth/register.rs index 44c29d8..d10915e 100644 --- a/src/api/auth/register.rs +++ b/src/api/auth/register.rs @@ -6,10 +6,10 @@ use serde_json::to_string; use crate::gateway::Gateway; use crate::types::GatewayIdentifyPayload; use crate::{ - api::policies::instance::LimitType, errors::ChorusResult, instance::{ChorusUser, Instance, Token}, ratelimiter::ChorusRequest, + types::LimitType, types::RegisterSchema, }; @@ -19,14 +19,14 @@ impl Instance { /// # Reference /// See pub async fn register_account( - &mut self, - register_schema: &RegisterSchema, + mut self, + register_schema: RegisterSchema, ) -> ChorusResult { let endpoint_url = self.urls.api.clone() + "/auth/register"; let chorus_request = ChorusRequest { request: Client::new() .post(endpoint_url) - .body(to_string(register_schema).unwrap()) + .body(to_string(®ister_schema).unwrap()) .header("Content-Type", "application/json"), limit_type: LimitType::AuthRegister, }; @@ -43,7 +43,7 @@ impl Instance { self.limits_information.as_mut().unwrap().ratelimits = shell.limits.unwrap(); } let user_object = self.get_user(token.clone(), None).await.unwrap(); - let settings = ChorusUser::get_settings(&token, &self.urls.api.clone(), self).await?; + let settings = ChorusUser::get_settings(&token, &self.urls.api.clone(), &mut self).await?; let mut identify = GatewayIdentifyPayload::common(); let gateway = Gateway::new(self.urls.wss.clone()).await.unwrap(); identify.token = token.clone(); @@ -54,7 +54,7 @@ impl Instance { self.clone_limits_if_some(), Arc::new(RwLock::new(settings)), Arc::new(RwLock::new(user_object)), - Arc::new(gateway), + gateway, ); Ok(user) } diff --git a/src/api/channels/channels.rs b/src/api/channels/channels.rs index 7250b0e..9560d74 100644 --- a/src/api/channels/channels.rs +++ b/src/api/channels/channels.rs @@ -3,11 +3,12 @@ use serde_json::to_string; use crate::types::{AddChannelRecipientSchema, ModifyChannelPositionsSchema}; use crate::{ - api::LimitType, errors::{ChorusError, ChorusResult}, instance::ChorusUser, ratelimiter::ChorusRequest, - types::{Channel, ChannelModifySchema, GetChannelMessagesSchema, Message, Snowflake}, + types::{ + Channel, ChannelModifySchema, GetChannelMessagesSchema, LimitType, Message, Snowflake, + }, }; impl Channel { diff --git a/src/api/channels/messages.rs b/src/api/channels/messages.rs index 960e492..6dfdfbf 100644 --- a/src/api/channels/messages.rs +++ b/src/api/channels/messages.rs @@ -3,13 +3,12 @@ use http::HeaderMap; use reqwest::{multipart, Client}; use serde_json::{from_value, to_string, Value}; -use crate::api::LimitType; use crate::errors::{ChorusError, ChorusResult}; use crate::instance::ChorusUser; use crate::ratelimiter::ChorusRequest; use crate::types::{ - Channel, CreateGreetMessage, Message, MessageAck, MessageModifySchema, MessageSearchEndpoint, - MessageSearchQuery, MessageSendSchema, Snowflake, + Channel, CreateGreetMessage, LimitType, Message, MessageAck, MessageModifySchema, + MessageSearchEndpoint, MessageSearchQuery, MessageSendSchema, Snowflake, }; impl Message { diff --git a/src/api/channels/permissions.rs b/src/api/channels/permissions.rs index 7fa8edd..5360890 100644 --- a/src/api/channels/permissions.rs +++ b/src/api/channels/permissions.rs @@ -2,11 +2,10 @@ use reqwest::Client; use serde_json::to_string; use crate::{ - api::LimitType, errors::{ChorusError, ChorusResult}, instance::ChorusUser, ratelimiter::ChorusRequest, - types::{self, PermissionOverwrite, Snowflake}, + types::{self, LimitType, PermissionOverwrite, Snowflake}, }; impl types::Channel { diff --git a/src/api/channels/reactions.rs b/src/api/channels/reactions.rs index 81c0366..94d3087 100644 --- a/src/api/channels/reactions.rs +++ b/src/api/channels/reactions.rs @@ -1,9 +1,8 @@ use crate::{ - api::LimitType, errors::ChorusResult, instance::ChorusUser, ratelimiter::ChorusRequest, - types::{self, PublicUser, Snowflake}, + types::{self, LimitType, PublicUser, Snowflake}, }; /// Useful metadata for working with [`types::Reaction`], bundled together nicely. diff --git a/src/api/guilds/guilds.rs b/src/api/guilds/guilds.rs index 8d5f0d6..b433c84 100644 --- a/src/api/guilds/guilds.rs +++ b/src/api/guilds/guilds.rs @@ -2,14 +2,13 @@ use reqwest::Client; use serde_json::from_str; use serde_json::to_string; -use crate::api::LimitType; use crate::errors::ChorusError; use crate::errors::ChorusResult; use crate::instance::ChorusUser; use crate::ratelimiter::ChorusRequest; use crate::types::{ Channel, ChannelCreateSchema, Guild, GuildBanCreateSchema, GuildBansQuery, GuildCreateSchema, - GuildMember, GuildMemberSearchSchema, GuildModifySchema, GuildPreview, + GuildMember, GuildMemberSearchSchema, GuildModifySchema, GuildPreview, LimitType, ModifyGuildMemberProfileSchema, ModifyGuildMemberSchema, UserProfileMetadata, }; use crate::types::{GuildBan, Snowflake}; diff --git a/src/api/guilds/member.rs b/src/api/guilds/member.rs index 01294bd..885ddf9 100644 --- a/src/api/guilds/member.rs +++ b/src/api/guilds/member.rs @@ -1,11 +1,10 @@ use reqwest::Client; use crate::{ - api::LimitType, errors::ChorusResult, instance::ChorusUser, ratelimiter::ChorusRequest, - types::{self, GuildMember, Snowflake}, + types::{self, GuildMember, LimitType, Snowflake}, }; impl types::GuildMember { diff --git a/src/api/guilds/roles.rs b/src/api/guilds/roles.rs index 17d6f7b..f131367 100644 --- a/src/api/guilds/roles.rs +++ b/src/api/guilds/roles.rs @@ -2,11 +2,12 @@ use reqwest::Client; use serde_json::to_string; use crate::{ - api::LimitType, errors::{ChorusError, ChorusResult}, instance::ChorusUser, ratelimiter::ChorusRequest, - types::{self, RoleCreateModifySchema, RoleObject, RolePositionUpdateSchema, Snowflake}, + types::{ + self, LimitType, RoleCreateModifySchema, RoleObject, RolePositionUpdateSchema, Snowflake, + }, }; impl types::RoleObject { diff --git a/src/api/invites/mod.rs b/src/api/invites/mod.rs index 658fb22..332570b 100644 --- a/src/api/invites/mod.rs +++ b/src/api/invites/mod.rs @@ -4,7 +4,7 @@ use serde_json::to_string; use crate::errors::ChorusResult; use crate::instance::ChorusUser; use crate::ratelimiter::ChorusRequest; -use crate::types::{CreateChannelInviteSchema, GuildInvite, Invite, Snowflake}; +use crate::types::{CreateChannelInviteSchema, GuildInvite, Invite, LimitType, Snowflake}; impl ChorusUser { /// Accepts an invite to a guild, group DM, or DM. @@ -26,7 +26,7 @@ impl ChorusUser { invite_code )) .header("Authorization", self.token()), - limit_type: super::LimitType::Global, + limit_type: LimitType::Global, }; if session_id.is_some() { request.request = request @@ -53,7 +53,7 @@ impl ChorusUser { .body(to_string(&code).unwrap()) .header("Authorization", self.token()) .header("Content-Type", "application/json"), - limit_type: super::LimitType::Global, + limit_type: LimitType::Global, } .deserialize_response::(self) .await @@ -81,7 +81,7 @@ impl ChorusUser { .header("Authorization", self.token()) .header("Content-Type", "application/json") .body(to_string(&create_channel_invite_schema).unwrap()), - limit_type: super::LimitType::Channel(channel_id), + limit_type: LimitType::Channel(channel_id), } .deserialize_response::(self) .await diff --git a/src/api/mod.rs b/src/api/mod.rs index 7329c50..ab3f9b9 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -3,7 +3,6 @@ pub use channels::messages::*; pub use guilds::*; pub use invites::*; pub use policies::instance::instance::*; -pub use policies::instance::ratelimits::*; pub use users::*; pub mod auth; diff --git a/src/api/policies/instance/instance.rs b/src/api/policies/instance/instance.rs index aa529ad..4de5fd8 100644 --- a/src/api/policies/instance/instance.rs +++ b/src/api/policies/instance/instance.rs @@ -19,7 +19,7 @@ impl Instance { Err(e) => { return Err(ChorusError::RequestFailed { url: endpoint_url, - error: e, + error: e.to_string(), }); } }; diff --git a/src/api/policies/instance/mod.rs b/src/api/policies/instance/mod.rs index 0a1f245..b3a9148 100644 --- a/src/api/policies/instance/mod.rs +++ b/src/api/policies/instance/mod.rs @@ -1,5 +1,3 @@ pub use instance::*; -pub use ratelimits::*; pub mod instance; -pub mod ratelimits; diff --git a/src/api/policies/mod.rs b/src/api/policies/mod.rs index d0c29f1..1d5ea99 100644 --- a/src/api/policies/mod.rs +++ b/src/api/policies/mod.rs @@ -1,3 +1 @@ -pub use instance::ratelimits::*; - pub mod instance; diff --git a/src/api/users/channels.rs b/src/api/users/channels.rs index 8d5f063..330b3e3 100644 --- a/src/api/users/channels.rs +++ b/src/api/users/channels.rs @@ -2,11 +2,10 @@ use reqwest::Client; use serde_json::to_string; use crate::{ - api::LimitType, errors::ChorusResult, instance::ChorusUser, ratelimiter::ChorusRequest, - types::{Channel, PrivateChannelCreateSchema}, + types::{Channel, LimitType, PrivateChannelCreateSchema}, }; impl ChorusUser { diff --git a/src/api/users/guilds.rs b/src/api/users/guilds.rs index d2d5b9e..6ffcdfc 100644 --- a/src/api/users/guilds.rs +++ b/src/api/users/guilds.rs @@ -1,11 +1,10 @@ use reqwest::Client; use serde_json::to_string; -use crate::api::LimitType; use crate::errors::ChorusResult; use crate::instance::ChorusUser; use crate::ratelimiter::ChorusRequest; -use crate::types::{GetUserGuildSchema, Guild, Snowflake}; +use crate::types::{GetUserGuildSchema, Guild, LimitType, Snowflake}; impl ChorusUser { /// Leaves a given guild. @@ -26,7 +25,7 @@ impl ChorusUser { .header("Authorization", self.token()) .header("Content-Type", "application/json") .body(to_string(&lurking).unwrap()), - limit_type: crate::api::LimitType::Guild(*guild_id), + limit_type: LimitType::Guild(*guild_id), } .handle_request_as_result(self) .await diff --git a/src/api/users/relationships.rs b/src/api/users/relationships.rs index 8988871..4f9602c 100644 --- a/src/api/users/relationships.rs +++ b/src/api/users/relationships.rs @@ -2,12 +2,12 @@ use reqwest::Client; use serde_json::to_string; use crate::{ - api::LimitType, errors::ChorusResult, instance::ChorusUser, ratelimiter::ChorusRequest, types::{ - self, CreateUserRelationshipSchema, FriendRequestSendSchema, RelationshipType, Snowflake, + self, CreateUserRelationshipSchema, FriendRequestSendSchema, LimitType, RelationshipType, + Snowflake, }, }; diff --git a/src/api/users/users.rs b/src/api/users/users.rs index 25ac6cd..0f31d6f 100644 --- a/src/api/users/users.rs +++ b/src/api/users/users.rs @@ -4,11 +4,10 @@ use reqwest::Client; use serde_json::to_string; use crate::{ - api::LimitType, errors::{ChorusError, ChorusResult}, instance::{ChorusUser, Instance}, ratelimiter::ChorusRequest, - types::{User, UserModifySchema, UserSettings}, + types::{LimitType, User, UserModifySchema, UserSettings}, }; impl ChorusUser { diff --git a/src/errors.rs b/src/errors.rs index 642a3ba..4099a6b 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,11 +1,10 @@ //! Contains all the errors that can be returned by the library. use custom_error::custom_error; -use reqwest::Error; use crate::types::WebSocketEvent; custom_error! { - #[derive(PartialEq, Eq)] + #[derive(PartialEq, Eq, Clone, Hash)] pub RegistrationError Consent = "Consent must be 'true' to register.", } @@ -13,11 +12,12 @@ custom_error! { pub type ChorusResult = std::result::Result; custom_error! { + #[derive(Clone, Hash, PartialEq, Eq)] pub ChorusError /// Server did not respond. NoResponse = "Did not receive a response from the Server.", /// Reqwest returned an Error instead of a Response object. - RequestFailed{url:String, error: Error} = "An error occured while trying to GET from {url}: {error}", + RequestFailed{url:String, error: String} = "An error occured while trying to GET from {url}: {error}", /// Response received, however, it was not of the successful responses type. Used when no other, special case applies. ReceivedErrorCode{error_code: u16, error: String} = "Received the following error code while requesting from the route: {error_code}", /// Used when there is likely something wrong with the instance, the request was directed to. diff --git a/src/gateway.rs b/src/gateway.rs index 6913e6a..8689406 100644 --- a/src/gateway.rs +++ b/src/gateway.rs @@ -5,7 +5,7 @@ use crate::gateway::events::Events; use crate::types::{ self, AutoModerationRule, AutoModerationRuleUpdate, Channel, ChannelCreate, ChannelDelete, ChannelUpdate, Composite, Guild, GuildRoleCreate, GuildRoleUpdate, JsonField, RoleObject, - Snowflake, ThreadUpdate, UpdateMessage, WebSocketEvent, + Snowflake, SourceUrlField, ThreadUpdate, UpdateMessage, WebSocketEvent, }; use async_trait::async_trait; use std::any::Any; @@ -158,7 +158,7 @@ pub type ObservableObject = dyn Send + Sync + Any; /// [`GatewayEvents`](GatewayEvent), which you can subscribe to. Gateway events include all currently /// implemented types with the trait [`WebSocketEvent`] /// Using this handle you can also send Gateway Events directly. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct GatewayHandle { pub url: String, pub events: Arc>, @@ -170,7 +170,6 @@ pub struct GatewayHandle { >, >, >, - pub handle: JoinHandle<()>, /// Tells gateway tasks to close kill_send: tokio::sync::broadcast::Sender<()>, pub(crate) store: Arc>>>>, @@ -293,7 +292,7 @@ impl GatewayHandle { /// Sends an update voice state to the server pub async fn send_update_voice_state(&self, to_send: types::UpdateVoiceState) { - let to_send_value = serde_json::to_value(&to_send).unwrap(); + let to_send_value = serde_json::to_value(to_send).unwrap(); trace!("GW: Sending Update Voice State.."); @@ -344,6 +343,7 @@ pub struct Gateway { websocket_receive: SplitStream>>, kill_send: tokio::sync::broadcast::Sender<()>, store: Arc>>>>, + url: String, } impl Gateway { @@ -407,10 +407,11 @@ impl Gateway { websocket_receive, kill_send: kill_send.clone(), store: store.clone(), + url: websocket_url.clone(), }; // Now we can continuously check for messages in a different task, since we aren't going to receive another hello - let handle: JoinHandle<()> = task::spawn(async move { + task::spawn(async move { gateway.gateway_listen_task().await; }); @@ -418,7 +419,6 @@ impl Gateway { url: websocket_url.clone(), events: shared_events, websocket_send: shared_websocket_send.clone(), - handle, kill_send: kill_send.clone(), store, }) @@ -501,7 +501,7 @@ impl Gateway { GATEWAY_DISPATCH => { let Some(event_name) = gateway_payload.event_name else { warn!("Gateway dispatch op without event_name"); - return + return; }; trace!("Gateway: Received {event_name}"); @@ -536,6 +536,7 @@ impl Gateway { let downcasted = unsafe { Arc::from_raw(ptr as *const RwLock<$update_type>).clone() }; drop(inner_object); message.set_json(json.to_string()); + message.set_source_url(self.url.clone()); message.update(downcasted.clone()); } else { warn!("Received {} for {}, but it has been observed to be a different type!", $name, id) @@ -916,7 +917,7 @@ impl GatewayEvent { } } -mod events { +pub mod events { use super::*; #[derive(Default, Debug)] diff --git a/src/instance.rs b/src/instance.rs index a3b5325..72bf350 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -8,15 +8,14 @@ use std::sync::{Arc, RwLock}; use reqwest::Client; use serde::{Deserialize, Serialize}; -use crate::api::{Limit, LimitType}; use crate::errors::ChorusResult; use crate::gateway::{Gateway, GatewayHandle}; use crate::ratelimiter::ChorusRequest; use crate::types::types::subconfigs::limits::rates::RateLimits; -use crate::types::{GeneralConfiguration, User, UserSettings}; +use crate::types::{GeneralConfiguration, Limit, LimitType, User, UserSettings}; use crate::UrlBundle; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] /// 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. pub struct Instance { @@ -26,7 +25,7 @@ pub struct Instance { pub client: Client, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct LimitsInformation { pub ratelimits: HashMap, pub configuration: RateLimits, @@ -94,7 +93,7 @@ pub struct ChorusUser { pub limits: Option>, pub settings: Arc>, pub object: Arc>, - pub gateway: Arc, // TODO: Can this be an Arc? That way we could have Clone implemented on ChorusUser + pub gateway: GatewayHandle, } impl ChorusUser { @@ -117,7 +116,7 @@ impl ChorusUser { limits: Option>, settings: Arc>, object: Arc>, - gateway: Arc, + gateway: GatewayHandle, ) -> ChorusUser { ChorusUser { belongs_to, @@ -139,7 +138,7 @@ impl ChorusUser { let object = Arc::new(RwLock::new(User::default())); let wss_url = instance.read().unwrap().urls.wss.clone(); // Dummy gateway object - let gateway = Arc::new(Gateway::new(wss_url).await.unwrap()); + let gateway = Gateway::new(wss_url).await.unwrap(); ChorusUser { token, belongs_to: instance.clone(), diff --git a/src/lib.rs b/src/lib.rs index 2edecc2..e63c41d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,7 +30,7 @@ pub mod types; #[cfg(feature = "client")] pub mod voice; -#[derive(Clone, Default, Debug, PartialEq, Eq)] +#[derive(Clone, Default, Debug, PartialEq, Eq, Hash)] /// A URLBundle bundles together the API-, Gateway- and CDN-URLs of a Spacebar instance. /// /// # Notes diff --git a/src/ratelimiter.rs b/src/ratelimiter.rs index 84bd641..88d4a02 100644 --- a/src/ratelimiter.rs +++ b/src/ratelimiter.rs @@ -8,10 +8,9 @@ use serde::Deserialize; use serde_json::from_str; use crate::{ - api::{Limit, LimitType}, errors::{ChorusError, ChorusResult}, instance::ChorusUser, - types::{types::subconfigs::limits::rates::RateLimits, LimitsConfiguration}, + types::{types::subconfigs::limits::rates::RateLimits, Limit, LimitType, LimitsConfiguration}, }; /// Chorus' request struct. This struct is used to send rate-limited requests to the Spacebar server. @@ -92,7 +91,7 @@ impl ChorusRequest { log::warn!("Request failed: {:?}", error); return Err(ChorusError::RequestFailed { url: error.url().unwrap().to_string(), - error, + error: error.to_string(), }); } }; @@ -360,7 +359,7 @@ impl ChorusRequest { Err(e) => { return Err(ChorusError::RequestFailed { url: url_api.to_string(), - error: e, + error: e.to_string(), }) } }; diff --git a/src/types/config/types/guild_configuration.rs b/src/types/config/types/guild_configuration.rs index 96e6ea8..65897ea 100644 --- a/src/types/config/types/guild_configuration.rs +++ b/src/types/config/types/guild_configuration.rs @@ -18,7 +18,7 @@ use crate::types::config::types::subconfigs::guild::{ }; use crate::types::{Error, GuildError}; -#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Hash)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum GuildFeatures { ActivitiesAlpha, @@ -139,7 +139,7 @@ pub enum GuildFeatures { InvitesClosed, } -#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, Eq)] +#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, Eq, Hash)] pub struct GuildFeaturesList(Vec); impl Deref for GuildFeaturesList { diff --git a/src/types/config/types/subconfigs/limits/rates.rs b/src/types/config/types/subconfigs/limits/rates.rs index ce1ea60..8fdd183 100644 --- a/src/types/config/types/subconfigs/limits/rates.rs +++ b/src/types/config/types/subconfigs/limits/rates.rs @@ -2,11 +2,9 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; -use crate::{ - api::LimitType, - types::config::types::subconfigs::limits::ratelimits::{ - route::RouteRateLimit, RateLimitOptions, - }, +use crate::types::{ + config::types::subconfigs::limits::ratelimits::{route::RouteRateLimit, RateLimitOptions}, + LimitType, }; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] diff --git a/src/types/entities/auto_moderation.rs b/src/types/entities/auto_moderation.rs index eaec2b0..a8910b1 100644 --- a/src/types/entities/auto_moderation.rs +++ b/src/types/entities/auto_moderation.rs @@ -1,13 +1,18 @@ use std::sync::{Arc, RwLock}; +#[cfg(feature = "client")] use crate::gateway::Updateable; + +#[cfg(feature = "client")] use chorus_macros::Updateable; + use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; use crate::types::utils::Snowflake; -#[derive(Serialize, Deserialize, Debug, Default, Clone, Updateable)] +#[cfg_attr(feature = "client", derive(Updateable))] +#[derive(Serialize, Deserialize, Debug, Default, Clone)] /// See pub struct AutoModerationRule { pub id: Snowflake, diff --git a/src/types/entities/channel.rs b/src/types/entities/channel.rs index 84530c9..280401c 100644 --- a/src/types/entities/channel.rs +++ b/src/types/entities/channel.rs @@ -1,21 +1,28 @@ use std::sync::{Arc, RwLock}; -use chorus_macros::{observe_option_vec, Composite, Updateable}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_aux::prelude::deserialize_string_from_number; use serde_repr::{Deserialize_repr, Serialize_repr}; use std::fmt::Debug; -use crate::gateway::{GatewayHandle, Updateable}; use crate::types::{ entities::{GuildMember, User}, utils::Snowflake, - Composite, }; -#[derive(Default, Debug, Serialize, Deserialize, Clone, Updateable, Composite)] +#[cfg(feature = "client")] +use crate::types::Composite; + +#[cfg(feature = "client")] +use crate::gateway::{GatewayHandle, Updateable}; + +#[cfg(feature = "client")] +use chorus_macros::{observe_option_vec, Composite, Updateable}; + +#[derive(Default, Debug, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] +#[cfg_attr(feature = "client", derive(Updateable, Composite))] /// Represents a guild or private channel /// /// # Reference @@ -60,13 +67,13 @@ pub struct Channel { #[cfg(feature = "sqlx")] pub permission_overwrites: Option>>, #[cfg(not(feature = "sqlx"))] - #[observe_option_vec] + #[cfg_attr(feature = "client", observe_option_vec)] pub permission_overwrites: Option>>>, pub permissions: Option, pub position: Option, pub rate_limit_per_user: Option, #[cfg_attr(feature = "sqlx", sqlx(skip))] - #[observe_option_vec] + #[cfg_attr(feature = "client", observe_option_vec)] pub recipients: Option>>>, pub rtc_region: Option, #[cfg_attr(feature = "sqlx", sqlx(skip))] @@ -126,9 +133,8 @@ pub struct Tag { pub emoji_name: Option, } -#[derive( - Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Updateable, Composite, -)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd)] +#[cfg_attr(feature = "client", derive(Updateable, Composite))] pub struct PermissionOverwrite { pub id: Snowflake, #[serde(rename = "type")] diff --git a/src/types/entities/emoji.rs b/src/types/entities/emoji.rs index d80e487..4f56af5 100644 --- a/src/types/entities/emoji.rs +++ b/src/types/entities/emoji.rs @@ -1,14 +1,22 @@ use std::fmt::Debug; use std::sync::{Arc, RwLock}; -use chorus_macros::{Composite, Updateable}; use serde::{Deserialize, Serialize}; -use crate::gateway::{GatewayHandle, Updateable}; use crate::types::entities::User; -use crate::types::{Composite, Snowflake}; +use crate::types::Snowflake; -#[derive(Debug, Clone, Deserialize, Serialize, Default, Updateable, Composite)] +#[cfg(feature = "client")] +use crate::types::Composite; + +#[cfg(feature = "client")] +use crate::gateway::{GatewayHandle, Updateable}; + +#[cfg(feature = "client")] +use chorus_macros::{Composite, Updateable}; + +#[derive(Debug, Clone, Deserialize, Serialize, Default)] +#[cfg_attr(feature = "client", derive(Updateable, Composite))] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] /// # Reference /// See @@ -27,6 +35,19 @@ pub struct Emoji { pub available: Option, } +impl std::hash::Hash for Emoji { + fn hash(&self, state: &mut H) { + self.id.hash(state); + self.name.hash(state); + self.roles.hash(state); + self.roles.hash(state); + self.require_colons.hash(state); + self.managed.hash(state); + self.animated.hash(state); + self.available.hash(state); + } +} + impl PartialEq for Emoji { fn eq(&self, other: &Self) -> bool { !(self.id != other.id diff --git a/src/types/entities/guild.rs b/src/types/entities/guild.rs index 93b13fc..bb4db0c 100644 --- a/src/types/entities/guild.rs +++ b/src/types/entities/guild.rs @@ -1,25 +1,32 @@ use std::fmt::Debug; use std::sync::{Arc, RwLock}; -use chorus_macros::{observe_option_vec, observe_vec, Composite, Updateable}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; -use crate::gateway::{GatewayHandle, Updateable}; use crate::types::types::guild_configuration::GuildFeaturesList; use crate::types::{ entities::{Channel, Emoji, RoleObject, Sticker, User, VoiceState, Webhook}, interfaces::WelcomeScreenObject, utils::Snowflake, - Composite, }; use bitflags::bitflags; use super::PublicUser; +#[cfg(feature = "client")] +use crate::gateway::{GatewayHandle, Updateable}; + +#[cfg(feature = "client")] +use chorus_macros::{observe_option_vec, observe_vec, Composite, Updateable}; + +#[cfg(feature = "client")] +use crate::types::Composite; + /// See -#[derive(Serialize, Deserialize, Debug, Default, Clone, Updateable, Composite)] +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +#[cfg_attr(feature = "client", derive(Updateable, Composite))] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] pub struct Guild { pub afk_channel_id: Option, @@ -34,14 +41,14 @@ pub struct Guild { #[cfg_attr(feature = "sqlx", sqlx(skip))] pub bans: Option>, #[cfg_attr(feature = "sqlx", sqlx(skip))] - #[observe_option_vec] + #[cfg_attr(feature = "client", observe_option_vec)] pub channels: Option>>>, pub default_message_notifications: Option, pub description: Option, pub discovery_splash: Option, #[cfg_attr(feature = "sqlx", sqlx(skip))] + #[cfg_attr(feature = "client", observe_vec)] #[serde(default)] - #[observe_vec] pub emojis: Vec>>, pub explicit_content_filter: Option, //#[cfg_attr(feature = "sqlx", sqlx(try_from = "String"))] @@ -77,7 +84,7 @@ pub struct Guild { pub public_updates_channel_id: Option, pub region: Option, #[cfg_attr(feature = "sqlx", sqlx(skip))] - #[observe_option_vec] + #[cfg_attr(feature = "client", observe_option_vec)] pub roles: Option>>>, #[cfg_attr(feature = "sqlx", sqlx(skip))] pub rules_channel: Option, @@ -91,10 +98,10 @@ pub struct Guild { pub vanity_url_code: Option, pub verification_level: Option, #[cfg_attr(feature = "sqlx", sqlx(skip))] - #[observe_option_vec] + #[cfg_attr(feature = "client", observe_option_vec)] pub voice_states: Option>>>, #[cfg_attr(feature = "sqlx", sqlx(skip))] - #[observe_option_vec] + #[cfg_attr(feature = "client", observe_option_vec)] pub webhooks: Option>>>, #[cfg(feature = "sqlx")] pub welcome_screen: Option>, @@ -104,8 +111,113 @@ pub struct Guild { pub widget_enabled: Option, } +impl std::hash::Hash for Guild { + fn hash(&self, state: &mut H) { + self.afk_channel_id.hash(state); + self.afk_timeout.hash(state); + self.application_id.hash(state); + self.approximate_member_count.hash(state); + self.approximate_presence_count.hash(state); + self.banner.hash(state); + self.bans.hash(state); + self.default_message_notifications.hash(state); + self.description.hash(state); + self.discovery_splash.hash(state); + self.explicit_content_filter.hash(state); + self.features.hash(state); + self.icon.hash(state); + self.icon_hash.hash(state); + self.id.hash(state); + self.invites.hash(state); + self.joined_at.hash(state); + self.large.hash(state); + self.max_members.hash(state); + self.max_presences.hash(state); + self.max_stage_video_channel_users.hash(state); + self.max_video_channel_users.hash(state); + self.mfa_level.hash(state); + self.name.hash(state); + self.nsfw_level.hash(state); + self.owner.hash(state); + self.owner_id.hash(state); + self.permissions.hash(state); + self.preferred_locale.hash(state); + self.premium_progress_bar_enabled.hash(state); + self.premium_subscription_count.hash(state); + self.premium_tier.hash(state); + self.primary_category_id.hash(state); + self.public_updates_channel_id.hash(state); + self.region.hash(state); + self.rules_channel.hash(state); + self.rules_channel_id.hash(state); + self.splash.hash(state); + self.stickers.hash(state); + self.system_channel_flags.hash(state); + self.system_channel_id.hash(state); + self.vanity_url_code.hash(state); + self.verification_level.hash(state); + self.welcome_screen.hash(state); + self.welcome_screen.hash(state); + self.widget_channel_id.hash(state); + self.widget_enabled.hash(state); + } +} + +impl std::cmp::PartialEq for Guild { + fn eq(&self, other: &Self) -> bool { + self.afk_channel_id == other.afk_channel_id + && self.afk_timeout == other.afk_timeout + && self.application_id == other.application_id + && self.approximate_member_count == other.approximate_member_count + && self.approximate_presence_count == other.approximate_presence_count + && self.banner == other.banner + && self.bans == other.bans + && self.default_message_notifications == other.default_message_notifications + && self.description == other.description + && self.discovery_splash == other.discovery_splash + && self.explicit_content_filter == other.explicit_content_filter + && self.features == other.features + && self.icon == other.icon + && self.icon_hash == other.icon_hash + && self.id == other.id + && self.joined_at == other.joined_at + && self.large == other.large + && self.max_members == other.max_members + && self.max_presences == other.max_presences + && self.max_stage_video_channel_users == other.max_stage_video_channel_users + && self.max_video_channel_users == other.max_video_channel_users + && self.mfa_level == other.mfa_level + && self.name == other.name + && self.nsfw_level == other.nsfw_level + && self.owner == other.owner + && self.owner_id == other.owner_id + && self.permissions == other.permissions + && self.preferred_locale == other.preferred_locale + && self.premium_progress_bar_enabled == other.premium_progress_bar_enabled + && self.premium_subscription_count == other.premium_subscription_count + && self.premium_tier == other.premium_tier + && self.primary_category_id == other.primary_category_id + && self.public_updates_channel_id == other.public_updates_channel_id + && self.region == other.region + && self.rules_channel == other.rules_channel + && self.rules_channel_id == other.rules_channel_id + && self.splash == other.splash + && self.stickers == other.stickers + && self.system_channel_flags == other.system_channel_flags + && self.system_channel_id == other.system_channel_id + && self.vanity_url_code == other.vanity_url_code + && self.verification_level == other.verification_level + && self.welcome_screen == other.welcome_screen + && self.welcome_screen == other.welcome_screen + && self.widget_channel_id == other.widget_channel_id + && self.widget_enabled == other.widget_enabled + } +} + +impl std::cmp::Eq for Guild {} + /// See -#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] pub struct GuildBan { pub user: PublicUser, @@ -135,10 +247,29 @@ pub struct GuildInvite { pub vanity_url: Option, } -#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] +impl std::hash::Hash for GuildInvite { + fn hash(&self, state: &mut H) { + self.code.hash(state); + self.temporary.hash(state); + self.uses.hash(state); + self.max_uses.hash(state); + self.max_age.hash(state); + self.created_at.hash(state); + self.expires_at.hash(state); + self.guild_id.hash(state); + self.channel_id.hash(state); + self.inviter_id.hash(state); + self.target_user_id.hash(state); + self.target_user.hash(state); + self.target_user_type.hash(state); + self.vanity_url.hash(state); + } +} + +#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Hash)] pub struct UnavailableGuild { - id: Snowflake, - unavailable: bool, + pub id: Snowflake, + pub unavailable: bool, } #[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] @@ -202,7 +333,7 @@ pub struct GuildScheduledEventEntityMetadata { pub location: Option, } -#[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq, Hash)] pub struct VoiceRegion { id: String, name: String, @@ -211,7 +342,7 @@ pub struct VoiceRegion { custom: bool, } -#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq)] +#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq, Hash, Copy)] #[repr(u8)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] /// See @@ -221,7 +352,7 @@ pub enum MessageNotificationLevel { OnlyMentions = 1, } -#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq)] +#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq, Hash, Copy)] #[repr(u8)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] /// See @@ -232,7 +363,7 @@ pub enum ExplicitContentFilterLevel { AllMembers = 2, } -#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq)] +#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq, Hash, Copy)] #[repr(u8)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] /// See @@ -245,7 +376,7 @@ pub enum VerificationLevel { VeryHigh = 4, } -#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq)] +#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq, Hash, Copy)] #[repr(u8)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] /// See @@ -255,7 +386,7 @@ pub enum MFALevel { Elevated = 1, } -#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq)] +#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq, Hash, Copy)] #[repr(u8)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] /// See @@ -267,7 +398,7 @@ pub enum NSFWLevel { AgeRestricted = 3, } -#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq)] +#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq, Hash, Copy)] #[repr(u8)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] /// See @@ -280,7 +411,7 @@ pub enum PremiumTier { } bitflags! { - #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] + #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] /// # Reference /// See pub struct SystemChannelFlags: u64 { diff --git a/src/types/entities/mod.rs b/src/types/entities/mod.rs index abdf976..a14ef2c 100644 --- a/src/types/entities/mod.rs +++ b/src/types/entities/mod.rs @@ -10,6 +10,7 @@ pub use guild_member::*; pub use integration::*; pub use invite::*; pub use message::*; +pub use ratelimits::*; pub use relationship::*; pub use role::*; pub use security_key::*; @@ -22,9 +23,16 @@ pub use user_settings::*; pub use voice_state::*; pub use webhook::*; +#[cfg(feature = "client")] use crate::gateway::{GatewayHandle, Updateable}; + +#[cfg(feature = "client")] use async_trait::async_trait; + +#[cfg(feature = "client")] use std::fmt::Debug; + +#[cfg(feature = "client")] use std::sync::{Arc, RwLock}; mod application; @@ -39,6 +47,7 @@ mod guild_member; mod integration; mod invite; mod message; +mod ratelimits; mod relationship; mod role; mod security_key; @@ -51,6 +60,7 @@ mod user_settings; mod voice_state; mod webhook; +#[cfg(feature = "client")] #[async_trait(?Send)] pub trait Composite { async fn watch_whole(self, gateway: &GatewayHandle) -> Self; diff --git a/src/api/policies/instance/ratelimits.rs b/src/types/entities/ratelimits.rs similarity index 100% rename from src/api/policies/instance/ratelimits.rs rename to src/types/entities/ratelimits.rs diff --git a/src/types/entities/role.rs b/src/types/entities/role.rs index 22dceff..087a775 100644 --- a/src/types/entities/role.rs +++ b/src/types/entities/role.rs @@ -1,13 +1,21 @@ use bitflags::bitflags; -use chorus_macros::{Composite, Updateable}; use serde::{Deserialize, Serialize}; use serde_aux::prelude::{deserialize_option_number_from_string, deserialize_string_from_number}; use std::fmt::Debug; -use crate::gateway::{GatewayHandle, Updateable}; -use crate::types::{utils::Snowflake, Composite}; +use crate::types::utils::Snowflake; -#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Updateable, Composite)] +#[cfg(feature = "client")] +use chorus_macros::{Composite, Updateable}; + +#[cfg(feature = "client")] +use crate::gateway::{GatewayHandle, Updateable}; + +#[cfg(feature = "client")] +use crate::types::Composite; + +#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] +#[cfg_attr(feature = "client", derive(Updateable, Composite))] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] /// See pub struct RoleObject { diff --git a/src/types/entities/sticker.rs b/src/types/entities/sticker.rs index c2cdb46..593206d 100644 --- a/src/types/entities/sticker.rs +++ b/src/types/entities/sticker.rs @@ -28,6 +28,22 @@ pub struct Sticker { pub sort_value: Option, } +impl std::hash::Hash for Sticker { + fn hash(&self, state: &mut H) { + self.id.hash(state); + self.pack_id.hash(state); + self.name.hash(state); + self.description.hash(state); + self.tags.hash(state); + self.asset.hash(state); + self.sticker_type.hash(state); + self.format_type.hash(state); + self.available.hash(state); + self.guild_id.hash(state); + self.sort_value.hash(state); + } +} + impl PartialEq for Sticker { fn eq(&self, other: &Self) -> bool { self.id == other.id diff --git a/src/types/entities/user.rs b/src/types/entities/user.rs index 64334ff..eca5344 100644 --- a/src/types/entities/user.rs +++ b/src/types/entities/user.rs @@ -1,11 +1,17 @@ -use chorus_macros::{Composite, Updateable}; +use crate::types::utils::Snowflake; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_aux::prelude::deserialize_option_number_from_string; use std::fmt::Debug; +#[cfg(feature = "client")] use crate::gateway::{GatewayHandle, Updateable}; -use crate::types::{utils::Snowflake, Composite}; + +#[cfg(feature = "client")] +use crate::types::Composite; + +#[cfg(feature = "client")] +use chorus_macros::{Composite, Updateable}; use super::Emoji; @@ -21,7 +27,8 @@ impl User { PublicUser::from(self) } } -#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Updateable, Composite)] +#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "client", derive(Updateable, Composite))] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] pub struct User { pub id: Snowflake, @@ -56,7 +63,7 @@ pub struct User { pub disabled: Option, } -#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] pub struct PublicUser { pub id: Snowflake, pub username: Option, diff --git a/src/types/entities/user_settings.rs b/src/types/entities/user_settings.rs index 1be2c0a..e6db7e7 100644 --- a/src/types/entities/user_settings.rs +++ b/src/types/entities/user_settings.rs @@ -53,15 +53,17 @@ pub struct UserSettings { pub friend_source_flags: sqlx::types::Json, #[cfg(not(feature = "sqlx"))] pub friend_source_flags: FriendSourceFlags, - pub gateway_connected: bool, + pub gateway_connected: Option, pub gif_auto_play: bool, #[cfg(feature = "sqlx")] pub guild_folders: sqlx::types::Json>, #[cfg(not(feature = "sqlx"))] pub guild_folders: Vec, #[cfg(feature = "sqlx")] + #[serde(default)] pub guild_positions: sqlx::types::Json>, #[cfg(not(feature = "sqlx"))] + #[serde(default)] pub guild_positions: Vec, pub inline_attachment_media: bool, pub inline_embed_media: bool, @@ -98,7 +100,7 @@ impl Default for UserSettings { enable_tts_command: false, explicit_content_filter: 0, friend_source_flags: Default::default(), - gateway_connected: false, + gateway_connected: Some(false), gif_auto_play: false, guild_folders: Default::default(), guild_positions: Default::default(), diff --git a/src/types/entities/voice_state.rs b/src/types/entities/voice_state.rs index c879c8e..e764296 100644 --- a/src/types/entities/voice_state.rs +++ b/src/types/entities/voice_state.rs @@ -1,20 +1,27 @@ use std::sync::{Arc, RwLock}; +#[cfg(feature = "client")] use chorus_macros::{Composite, Updateable}; + +#[cfg(feature = "client")] +use crate::types::Composite; + +#[cfg(feature = "client")] +use crate::gateway::{GatewayHandle, Updateable}; + use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use std::fmt::Debug; -use crate::gateway::{GatewayHandle, Updateable}; use crate::types::{ entities::{Guild, GuildMember}, utils::Snowflake, - Composite, }; /// See -#[derive(Serialize, Deserialize, Debug, Default, Clone, Updateable, Composite)] +#[derive(Serialize, Deserialize, Debug, Default, Clone)] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] +#[cfg_attr(feature = "client", derive(Updateable, Composite))] pub struct VoiceState { pub guild_id: Option, pub guild: Option, diff --git a/src/types/entities/webhook.rs b/src/types/entities/webhook.rs index 7771dbf..b544ec9 100644 --- a/src/types/entities/webhook.rs +++ b/src/types/entities/webhook.rs @@ -1,18 +1,25 @@ use std::fmt::Debug; use std::sync::{Arc, RwLock}; -use chorus_macros::{Composite, Updateable}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "client")] use crate::gateway::{GatewayHandle, Updateable}; + +#[cfg(feature = "client")] +use chorus_macros::{Composite, Updateable}; + +#[cfg(feature = "client")] +use crate::types::Composite; + use crate::types::{ entities::{Guild, User}, utils::Snowflake, - Composite, }; /// See -#[derive(Serialize, Deserialize, Debug, Default, Clone, Updateable, Composite)] +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +#[cfg_attr(feature = "client", derive(Updateable, Composite))] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] pub struct Webhook { pub id: Snowflake, diff --git a/src/types/events/auto_moderation.rs b/src/types/events/auto_moderation.rs index 376e523..2a2eb6b 100644 --- a/src/types/events/auto_moderation.rs +++ b/src/types/events/auto_moderation.rs @@ -1,5 +1,5 @@ -use crate::types::JsonField; -use chorus_macros::JsonField; +use crate::types::{JsonField, SourceUrlField}; +use chorus_macros::{JsonField, SourceUrlField}; use serde::{Deserialize, Serialize}; use crate::types::{ @@ -7,6 +7,7 @@ use crate::types::{ WebSocketEvent, }; +#[cfg(feature = "client")] use super::UpdateMessage; #[derive(Debug, Deserialize, Serialize, Default, Clone)] @@ -18,15 +19,18 @@ pub struct AutoModerationRuleCreate { impl WebSocketEvent for AutoModerationRuleCreate {} -#[derive(Debug, Deserialize, Serialize, Default, Clone, JsonField)] +#[derive(Debug, Deserialize, Serialize, Default, Clone, JsonField, SourceUrlField)] /// See pub struct AutoModerationRuleUpdate { #[serde(flatten)] pub rule: AutoModerationRule, #[serde(skip)] pub json: String, + #[serde(skip)] + pub source_url: String, } +#[cfg(feature = "client")] impl UpdateMessage for AutoModerationRuleUpdate { fn id(&self) -> Option { Some(self.rule.id) diff --git a/src/types/events/channel.rs b/src/types/events/channel.rs index ae84b79..eb557d7 100644 --- a/src/types/events/channel.rs +++ b/src/types/events/channel.rs @@ -1,14 +1,18 @@ -use std::sync::{Arc, RwLock}; - use crate::types::events::WebSocketEvent; -use crate::types::Guild; -use crate::types::{entities::Channel, JsonField, Snowflake}; -use chorus_macros::JsonField; +use crate::types::{entities::Channel, JsonField, Snowflake, SourceUrlField}; +use chorus_macros::{JsonField, SourceUrlField}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "client")] use super::UpdateMessage; +#[cfg(feature = "client")] +use std::sync::{Arc, RwLock}; + +#[cfg(feature = "client")] +use crate::types::Guild; + #[derive(Debug, Default, Deserialize, Serialize)] /// See pub struct ChannelPinsUpdate { @@ -19,17 +23,20 @@ pub struct ChannelPinsUpdate { impl WebSocketEvent for ChannelPinsUpdate {} -#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField)] +#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField)] /// See pub struct ChannelCreate { #[serde(flatten)] pub channel: Channel, #[serde(skip)] pub json: String, + #[serde(skip)] + pub source_url: String, } impl WebSocketEvent for ChannelCreate {} +#[cfg(feature = "client")] impl UpdateMessage for ChannelCreate { fn id(&self) -> Option { self.channel.guild_id @@ -46,17 +53,20 @@ impl UpdateMessage for ChannelCreate { } } -#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField)] +#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField)] /// See pub struct ChannelUpdate { #[serde(flatten)] pub channel: Channel, #[serde(skip)] pub json: String, + #[serde(skip)] + pub source_url: String, } impl WebSocketEvent for ChannelUpdate {} +#[cfg(feature = "client")] impl UpdateMessage for ChannelUpdate { fn update(&mut self, object_to_update: Arc>) { let mut write = object_to_update.write().unwrap(); @@ -87,15 +97,18 @@ pub struct ChannelUnreadUpdateObject { impl WebSocketEvent for ChannelUnreadUpdate {} -#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField)] +#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField)] /// See pub struct ChannelDelete { #[serde(flatten)] pub channel: Channel, #[serde(skip)] pub json: String, + #[serde(skip)] + pub source_url: String, } +#[cfg(feature = "client")] impl UpdateMessage for ChannelDelete { fn id(&self) -> Option { self.channel.guild_id @@ -111,11 +124,7 @@ impl UpdateMessage for ChannelDelete { } for (iteration, item) in (0_u32..).zip(write.channels.as_mut().unwrap().iter()) { if item.read().unwrap().id == self.id().unwrap() { - write - .channels - .as_mut() - .unwrap() - .swap_remove(iteration as usize); + write.channels.as_mut().unwrap().remove(iteration as usize); return; } } diff --git a/src/types/events/guild.rs b/src/types/events/guild.rs index 0afa9d6..0c6bb05 100644 --- a/src/types/events/guild.rs +++ b/src/types/events/guild.rs @@ -1,6 +1,4 @@ -use std::sync::{Arc, RwLock}; - -use chorus_macros::JsonField; +use chorus_macros::{JsonField, SourceUrlField}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -8,18 +6,38 @@ use crate::types::entities::{Guild, PublicUser, UnavailableGuild}; use crate::types::events::WebSocketEvent; use crate::types::{ AuditLogEntry, Emoji, GuildMember, GuildScheduledEvent, JsonField, RoleObject, Snowflake, - Sticker, + SourceUrlField, Sticker, }; -use super::{PresenceUpdate, UpdateMessage}; +use super::PresenceUpdate; -#[derive(Debug, Deserialize, Serialize, Default, Clone)] +#[cfg(feature = "client")] +use super::UpdateMessage; +#[cfg(feature = "client")] +use std::sync::{Arc, RwLock}; + +#[derive(Debug, Deserialize, Serialize, Default, Clone, SourceUrlField, JsonField)] /// See ; /// Received to give data about a guild; // This one is particularly painful, it can be a Guild object with an extra field or an unavailable guild object pub struct GuildCreate { #[serde(flatten)] pub d: GuildCreateDataOption, + #[serde(skip)] + pub source_url: String, + #[serde(skip)] + pub json: String, +} + +impl UpdateMessage for GuildCreate { + fn id(&self) -> Option { + match &self.d { + GuildCreateDataOption::UnavailableGuild(unavailable) => Some(unavailable.id), + GuildCreateDataOption::Guild(guild) => Some(guild.id), + } + } + + fn update(&mut self, _: Arc>) {} } #[derive(Debug, Deserialize, Serialize, Clone)] @@ -57,22 +75,43 @@ pub struct GuildBanRemove { impl WebSocketEvent for GuildBanRemove {} -#[derive(Debug, Default, Deserialize, Serialize, Clone)] +#[derive(Debug, Default, Deserialize, Serialize, Clone, SourceUrlField, JsonField)] /// See ; /// Received to give info about a guild being updated; pub struct GuildUpdate { #[serde(flatten)] pub guild: Guild, + #[serde(skip)] + pub source_url: String, + #[serde(skip)] + pub json: String, } impl WebSocketEvent for GuildUpdate {} -#[derive(Debug, Default, Deserialize, Serialize, Clone)] +impl UpdateMessage for GuildUpdate { + fn id(&self) -> Option { + Some(self.guild.id) + } +} + +#[derive(Debug, Default, Deserialize, Serialize, Clone, SourceUrlField, JsonField)] /// See ; /// Received to tell the client about a guild being deleted; pub struct GuildDelete { #[serde(flatten)] pub guild: UnavailableGuild, + #[serde(skip)] + pub source_url: String, + #[serde(skip)] + pub json: String, +} + +impl UpdateMessage for GuildDelete { + fn id(&self) -> Option { + Some(self.guild.id) + } + fn update(&mut self, _: Arc>) {} } impl WebSocketEvent for GuildDelete {} @@ -168,17 +207,20 @@ pub struct GuildMembersChunk { impl WebSocketEvent for GuildMembersChunk {} -#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField)] +#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField)] /// See pub struct GuildRoleCreate { pub guild_id: Snowflake, pub role: RoleObject, #[serde(skip)] pub json: String, + #[serde(skip)] + pub source_url: String, } impl WebSocketEvent for GuildRoleCreate {} +#[cfg(feature = "client")] impl UpdateMessage for GuildRoleCreate { fn id(&self) -> Option { Some(self.guild_id) @@ -198,17 +240,20 @@ impl UpdateMessage for GuildRoleCreate { } } -#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField)] +#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField)] /// See pub struct GuildRoleUpdate { pub guild_id: Snowflake, pub role: RoleObject, #[serde(skip)] pub json: String, + #[serde(skip)] + pub source_url: String, } impl WebSocketEvent for GuildRoleUpdate {} +#[cfg(feature = "client")] impl UpdateMessage for GuildRoleUpdate { fn id(&self) -> Option { Some(self.role.id) diff --git a/src/types/events/mod.rs b/src/types/events/mod.rs index e440c05..f4e926c 100644 --- a/src/types/events/mod.rs +++ b/src/types/events/mod.rs @@ -1,12 +1,5 @@ -use std::sync::{Arc, RwLock}; - -use std::collections::HashMap; - -use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; -use serde_json::{from_str, from_value, to_value, Value}; - pub use application::*; pub use auto_moderation::*; pub use call::*; @@ -34,9 +27,24 @@ pub use voice::*; pub use webhooks::*; pub use webrtc::*; +#[cfg(feature = "client")] +use super::Snowflake; + +#[cfg(feature = "client")] use crate::gateway::Updateable; -use super::Snowflake; +#[cfg(feature = "client")] +use serde_json::{from_str, from_value, to_value, Value}; + +#[cfg(feature = "client")] +use std::collections::HashMap; + +use std::fmt::Debug; +#[cfg(feature = "client")] +use std::sync::{Arc, RwLock}; + +#[cfg(feature = "client")] +use serde::de::DeserializeOwned; mod application; mod auto_moderation; @@ -66,7 +74,7 @@ mod webhooks; mod webrtc; -pub trait WebSocketEvent {} +pub trait WebSocketEvent: Send + Sync + Debug {} #[derive(Debug, Default, Serialize, Clone)] /// The payload used for sending events to the gateway @@ -107,6 +115,7 @@ pub struct GatewayReceivePayload<'a> { impl<'a> WebSocketEvent for GatewayReceivePayload<'a> {} +#[cfg(feature = "client")] /// An [`UpdateMessage`] represents a received Gateway Message which contains updated /// information for an [`Updateable`] of Type T. /// # Example: @@ -119,7 +128,7 @@ impl<'a> WebSocketEvent for GatewayReceivePayload<'a> {} /// This would imply, that the [`WebSocketEvent`] "[`ChannelUpdate`]" contains new/updated information /// about a [`Channel`]. The update method describes how this new information will be turned into /// a [`Channel`] object. -pub(crate) trait UpdateMessage: Clone + JsonField +pub(crate) trait UpdateMessage: Clone + JsonField + SourceUrlField where T: Updateable + Serialize + DeserializeOwned + Clone, { @@ -134,6 +143,12 @@ pub(crate) trait JsonField: Clone { fn get_json(&self) -> String; } +pub trait SourceUrlField: Clone { + fn set_source_url(&mut self, url: String); + fn get_source_url(&self) -> String; +} + +#[cfg(feature = "client")] /// Only applicable for events where the Update struct is the same as the Entity struct pub(crate) fn update_object( value: String, diff --git a/src/types/events/thread.rs b/src/types/events/thread.rs index 19f1cde..cff5f6f 100644 --- a/src/types/events/thread.rs +++ b/src/types/events/thread.rs @@ -1,10 +1,11 @@ -use chorus_macros::JsonField; +use chorus_macros::{JsonField, SourceUrlField}; use serde::{Deserialize, Serialize}; use crate::types::entities::{Channel, ThreadMember}; use crate::types::events::WebSocketEvent; -use crate::types::{JsonField, Snowflake}; +use crate::types::{JsonField, Snowflake, SourceUrlField}; +#[cfg(feature = "client")] use super::UpdateMessage; #[derive(Debug, Default, Deserialize, Serialize, Clone)] @@ -16,17 +17,20 @@ pub struct ThreadCreate { impl WebSocketEvent for ThreadCreate {} -#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField)] +#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField)] /// See pub struct ThreadUpdate { #[serde(flatten)] pub thread: Channel, #[serde(skip)] pub json: String, + #[serde(skip)] + pub source_url: String, } impl WebSocketEvent for ThreadUpdate {} +#[cfg(feature = "client")] impl UpdateMessage for ThreadUpdate { fn id(&self) -> Option { Some(self.thread.id) diff --git a/src/types/interfaces/guild_welcome_screen.rs b/src/types/interfaces/guild_welcome_screen.rs index 4912a78..dbeef0f 100644 --- a/src/types/interfaces/guild_welcome_screen.rs +++ b/src/types/interfaces/guild_welcome_screen.rs @@ -2,14 +2,14 @@ use serde::{Deserialize, Serialize}; use crate::types::utils::Snowflake; -#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Hash)] pub struct WelcomeScreenObject { pub enabled: bool, pub description: Option, pub welcome_channels: Vec, } -#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Hash)] pub struct WelcomeScreenChannel { pub channel_id: Snowflake, pub description: String, diff --git a/src/types/schema/auth.rs b/src/types/schema/auth.rs index 60e23a4..2159de9 100644 --- a/src/types/schema/auth.rs +++ b/src/types/schema/auth.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub struct RegisterSchema { pub username: String, @@ -15,7 +15,7 @@ pub struct RegisterSchema { pub promotional_email_opt_in: Option, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub struct LoginSchema { /// For Discord, usernames must be between 2 and 32 characters, diff --git a/tests/auth.rs b/tests/auth.rs index c26552f..f89e5e4 100644 --- a/tests/auth.rs +++ b/tests/auth.rs @@ -4,13 +4,13 @@ mod common; #[tokio::test] async fn test_registration() { - let mut bundle = common::setup().await; + let bundle = common::setup().await; let reg = RegisterSchema { username: "Hiiii".into(), date_of_birth: Some("2000-01-01".to_string()), consent: true, ..Default::default() }; - bundle.instance.register_account(®).await.unwrap(); + bundle.instance.clone().register_account(reg).await.unwrap(); common::teardown(bundle).await; } diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 19c6509..ce42578 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -31,7 +31,8 @@ impl TestBundle { ..Default::default() }; self.instance - .register_account(®ister_schema) + .clone() + .register_account(register_schema) .await .unwrap() } @@ -42,7 +43,7 @@ impl TestBundle { limits: self.user.limits.clone(), settings: self.user.settings.clone(), object: self.user.object.clone(), - gateway: Arc::new(Gateway::new(self.instance.urls.wss.clone()).await.unwrap()), + gateway: Gateway::new(self.instance.urls.wss.clone()).await.unwrap(), } } } @@ -54,7 +55,7 @@ pub(crate) async fn setup() -> TestBundle { "ws://localhost:3001".to_string(), "http://localhost:3001".to_string(), ); - let mut instance = Instance::new(urls.clone(), true).await.unwrap(); + let instance = Instance::new(urls.clone(), true).await.unwrap(); // Requires the existance of the below user. let reg = RegisterSchema { username: "integrationtestuser".into(), @@ -91,7 +92,7 @@ pub(crate) async fn setup() -> TestBundle { default_thread_rate_limit_per_user: Some(0), video_quality_mode: None, }; - let mut user = instance.register_account(®).await.unwrap(); + let mut user = instance.clone().register_account(reg).await.unwrap(); let guild = Guild::create(&mut user, guild_create_schema).await.unwrap(); let channel = Channel::create(&mut user, guild.id, None, channel_create_schema) .await