From a4be25887b9c1e8028e25e7157564b5a2b42ddf5 Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Thu, 15 Jun 2023 18:48:01 +0200 Subject: [PATCH 01/34] Implement test_get_mutual_relationships --- tests/relationships.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/relationships.rs diff --git a/tests/relationships.rs b/tests/relationships.rs new file mode 100644 index 0000000..08f7efb --- /dev/null +++ b/tests/relationships.rs @@ -0,0 +1,30 @@ +use chorus::types; + +mod common; + +#[tokio::test] +async fn test_get_mutual_relationships() { + let register_schema = types::RegisterSchema::new( + "integrationtestuser2".to_string(), + None, + true, + None, + None, + None, + Some("2000-01-01".to_string()), + None, + None, + None, + ) + .unwrap(); + + let bundle = common::setup().await; + let mut belongs_to = bundle.instance; + let mut user = bundle.user; + let other_user = belongs_to.register_account(®ister_schema).await.unwrap(); + let relationships = user + .get_mutual_relationships(&other_user.object.id.to_string()) + .await + .unwrap(); + println!("{:?}", relationships.unwrap()); +} From e971ed2b49f412cac95a261e28c76eaf7b288e90 Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Thu, 15 Jun 2023 19:00:47 +0200 Subject: [PATCH 02/34] Implement send_friend_request --- src/api/users/relationships.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/api/users/relationships.rs b/src/api/users/relationships.rs index d286ef6..ede39f0 100644 --- a/src/api/users/relationships.rs +++ b/src/api/users/relationships.rs @@ -1,6 +1,12 @@ use reqwest::Client; +use serde_json::to_string; -use crate::{api::deserialize_response, errors::ChorusLibError, instance::UserMeta, types}; +use crate::{ + api::{deserialize_response, handle_request_as_option}, + errors::ChorusLibError, + instance::UserMeta, + types, +}; impl UserMeta { /// Retrieves the mutual relationships between the authenticated user and the specified user. @@ -30,4 +36,24 @@ impl UserMeta { ) .await } + + /// Sends a friend request to a user. + /// + /// # Arguments + /// + /// * `schema` - A [`FriendRequestSendSchema`] struct that holds the information about the friend request to be sent. + /// + /// # Returns + /// This function returns an [`Option`] that holds a [`ChorusLibError`] if the request fails. + pub async fn send_friend_request( + &mut self, + schema: types::FriendRequestSendSchema, + ) -> Option { + let belongs_to = self.belongs_to.borrow(); + let url = format!("{}/users/@me/relationships/", belongs_to.urls.get_api()); + drop(belongs_to); + let body = to_string(&schema).unwrap(); + let request = Client::new().post(url).bearer_auth(self.token()).body(body); + handle_request_as_option(request, self, crate::api::limits::LimitType::Global).await + } } From cd303b0fefc272d9914ababe8bae1bf879a7e6c8 Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Thu, 15 Jun 2023 19:01:01 +0200 Subject: [PATCH 03/34] Add FriendRequestSendSchema --- src/types/entities/relationship.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/types/entities/relationship.rs b/src/types/entities/relationship.rs index b965907..fb8acee 100644 --- a/src/types/entities/relationship.rs +++ b/src/types/entities/relationship.rs @@ -30,3 +30,9 @@ pub enum RelationshipType { Friends = 1, None = 0, } + +#[derive(Deserialize, Serialize, Debug)] +pub struct FriendRequestSendSchema { + pub username: String, + pub discriminator: Option, +} From 8fb0e08d4ca461eb538cebbae94d39d766ab6f04 Mon Sep 17 00:00:00 2001 From: Flori <39242991+bitfl0wer@users.noreply.github.com> Date: Sat, 17 Jun 2023 23:26:57 +0200 Subject: [PATCH 04/34] Update README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 82ac173..4917884 100644 --- a/README.md +++ b/README.md @@ -93,9 +93,6 @@ - [x] Sending rich content in messages (links, images, videos) - [ ] Customizing embed appearance (title, description, color, fields) -### Notifications and Push Notifications -- [ ] Notification settings management - ### Webhooks - [ ] Webhook creation and management - [ ] Handling incoming webhook events From fb49c589af6c4d5ba3490b11a9901325b5ae38de Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Sun, 18 Jun 2023 13:59:11 +0200 Subject: [PATCH 05/34] Send a friend request --- tests/relationships.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/relationships.rs b/tests/relationships.rs index 08f7efb..44de72d 100644 --- a/tests/relationships.rs +++ b/tests/relationships.rs @@ -21,7 +21,12 @@ async fn test_get_mutual_relationships() { let bundle = common::setup().await; let mut belongs_to = bundle.instance; let mut user = bundle.user; - let other_user = belongs_to.register_account(®ister_schema).await.unwrap(); + let mut other_user = belongs_to.register_account(®ister_schema).await.unwrap(); + let friend_request_schema = types::FriendRequestSendSchema { + username: user.object.username.clone(), + discriminator: Some(user.object.discriminator.clone()), + }; + other_user.send_friend_request(friend_request_schema).await; let relationships = user .get_mutual_relationships(&other_user.object.id.to_string()) .await From fc6b431ad86e1a5c0e79f1493f4c75e7c9ad8200 Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Sun, 18 Jun 2023 14:00:12 +0200 Subject: [PATCH 06/34] Start implementing get_relationship --- src/api/users/relationships.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/api/users/relationships.rs b/src/api/users/relationships.rs index ede39f0..563b846 100644 --- a/src/api/users/relationships.rs +++ b/src/api/users/relationships.rs @@ -37,6 +37,19 @@ impl UserMeta { .await } + pub async fn get_relationships(&mut self) -> Result, ChorusLibError> { + let belongs_to = self.belongs_to.borrow(); + let url = format!("{}/users/@me/relationships/", belongs_to.urls.get_api(),); + drop(belongs_to); + let request = Client::new().get(url).bearer_auth(self.token()); + deserialize_response::>( + request, + self, + crate::api::limits::LimitType::Global, + ) + .await + } + /// Sends a friend request to a user. /// /// # Arguments From 6ea587f096c8916eb2621c40dd9928c214c7a05a Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Sun, 18 Jun 2023 15:39:40 +0200 Subject: [PATCH 07/34] Modift get_mutual, add get_relationships --- tests/relationships.rs | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/tests/relationships.rs b/tests/relationships.rs index 44de72d..b575046 100644 --- a/tests/relationships.rs +++ b/tests/relationships.rs @@ -18,9 +18,9 @@ async fn test_get_mutual_relationships() { ) .unwrap(); - let bundle = common::setup().await; - let mut belongs_to = bundle.instance; - let mut user = bundle.user; + let mut bundle = common::setup().await; + let belongs_to = &mut bundle.instance; + let user = &mut bundle.user; let mut other_user = belongs_to.register_account(®ister_schema).await.unwrap(); let friend_request_schema = types::FriendRequestSendSchema { username: user.object.username.clone(), @@ -31,5 +31,36 @@ async fn test_get_mutual_relationships() { .get_mutual_relationships(&other_user.object.id.to_string()) .await .unwrap(); - println!("{:?}", relationships.unwrap()); + println!("{:?}", relationships); + common::teardown(bundle).await +} + +#[tokio::test] +async fn test_get_relationships() { + let register_schema = types::RegisterSchema::new( + "integrationtestuser2".to_string(), + None, + true, + None, + None, + None, + Some("2000-01-01".to_string()), + None, + None, + None, + ) + .unwrap(); + + let mut bundle = common::setup().await; + let belongs_to = &mut bundle.instance; + let user = &mut bundle.user; + let mut other_user = belongs_to.register_account(®ister_schema).await.unwrap(); + let friend_request_schema = types::FriendRequestSendSchema { + username: user.object.username.clone(), + discriminator: Some(user.object.discriminator.clone()), + }; + other_user.send_friend_request(friend_request_schema).await; + let relationships = user.get_relationships().await.unwrap(); + println!("{:?}", relationships); + common::teardown(bundle).await } From 0460a67bdd68de5ed66007ba5876a00b13cbf775 Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Sun, 18 Jun 2023 15:39:52 +0200 Subject: [PATCH 08/34] Change returns, add documentation --- src/api/users/relationships.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/api/users/relationships.rs b/src/api/users/relationships.rs index 563b846..a678da9 100644 --- a/src/api/users/relationships.rs +++ b/src/api/users/relationships.rs @@ -16,11 +16,11 @@ impl UserMeta { /// * `user_id` - A string slice that holds the ID of the user to retrieve the mutual relationships with. /// /// # Returns - /// This function returns a [`Option>>`]. + /// This function returns a [`Result, ChorusLibError>`]. pub async fn get_mutual_relationships( &mut self, user_id: &str, - ) -> Result>, ChorusLibError> { + ) -> Result, ChorusLibError> { let belongs_to = self.belongs_to.borrow(); let url = format!( "{}/users/{}/relationships/", @@ -29,7 +29,7 @@ impl UserMeta { ); drop(belongs_to); let request = Client::new().get(url).bearer_auth(self.token()); - deserialize_response::>>( + deserialize_response::>( request, self, crate::api::limits::LimitType::Global, @@ -37,6 +37,10 @@ impl UserMeta { .await } + /// Retrieves the authenticated user's relationships. + /// + /// # Returns + /// This function returns a [`Result, ChorusLibError>`]. pub async fn get_relationships(&mut self) -> Result, ChorusLibError> { let belongs_to = self.belongs_to.borrow(); let url = format!("{}/users/@me/relationships/", belongs_to.urls.get_api(),); From 191dc34933bed2d579ac6eca0a28d956085d75a6 Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Sun, 18 Jun 2023 19:04:55 +0200 Subject: [PATCH 09/34] Create relationship schemes --- src/types/schema/relationship.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/types/schema/relationship.rs b/src/types/schema/relationship.rs index 8b13789..b0a60d6 100644 --- a/src/types/schema/relationship.rs +++ b/src/types/schema/relationship.rs @@ -1 +1,25 @@ +use serde::{Deserialize, Serialize}; +use crate::types::RelationshipType; + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct FriendRequestSendSchema { + pub username: String, + pub discriminator: Option, +} + +/// Represents the schema for the Create User Relationship route. +/// # Arguments +/// +/// * relationship_type: The [`RelationshipType`] to create (defaults to -1, which accepts an existing or creates a new friend request) +/// * from_friend_suggestion: Whether the relationship was created from a friend suggestion (default false) +/// * friend_token: The friend token of the user to add a direct friend relationship to +/// +/// See: [https://discord-userdoccers.vercel.app/resources/user#create-user-relationship](https://discord-userdoccers.vercel.app/resources/user#create-user-relationship) +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct CreateUserRelationshipSchema { + #[serde(rename = "type")] + pub relationship_type: Option, + pub from_friend_suggestion: Option, + pub friend_token: Option, +} From 4829845512b2bb20f9b6bd5a2d3caf74e9d8ec6f Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Sun, 18 Jun 2023 19:05:09 +0200 Subject: [PATCH 10/34] Move FriendRequestSchema to schemas --- src/types/entities/relationship.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/types/entities/relationship.rs b/src/types/entities/relationship.rs index fb8acee..b965907 100644 --- a/src/types/entities/relationship.rs +++ b/src/types/entities/relationship.rs @@ -30,9 +30,3 @@ pub enum RelationshipType { Friends = 1, None = 0, } - -#[derive(Deserialize, Serialize, Debug)] -pub struct FriendRequestSendSchema { - pub username: String, - pub discriminator: Option, -} From 6878fd0380026fa00b4e44e86447a69188e05735 Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Sun, 18 Jun 2023 23:01:38 +0200 Subject: [PATCH 11/34] Fix errors not displayed, add InvalidArgumentsErr --- src/errors.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 0f65669..057f57f 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -18,13 +18,14 @@ custom_error! { CantGetInfoError{error:String} = "Something seems to be wrong with the instance. Cannot get information about the instance: {error}", InvalidFormBodyError{error_type: String, error:String} = "The server responded with: {error_type}: {error}", RateLimited{bucket:String} = "Ratelimited on Bucket {bucket}", - MultipartCreationError{error: String} = "Got an error whilst creating the form: {}", - FormCreationError{error: String} = "Got an error whilst creating the form: {}", + MultipartCreationError{error: String} = "Got an error whilst creating the form: {error}", + FormCreationError{error: String} = "Got an error whilst creating the form: {error}", TokenExpired = "Token expired, invalid or not found.", NoPermission = "You do not have the permissions needed to perform this action.", - NotFound{error: String} = "The provided resource hasn't been found: {}", + NotFound{error: String} = "The provided resource hasn't been found: {error}", PasswordRequiredError = "You need to provide your current password to authenticate for this action.", - InvalidResponseError{error: String} = "The response is malformed and cannot be processed. Error: {}", + InvalidResponseError{error: String} = "The response is malformed and cannot be processed. Error: {error}", + InvalidArgumentsError{error: String} = "Invalid arguments were provided. Error: {error}" } custom_error! { From 636cb4c7518d7d3ac89f37e587327121f74e1e89 Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Sun, 18 Jun 2023 23:02:49 +0200 Subject: [PATCH 12/34] Add modify_user_relationship --- src/api/users/relationships.rs | 61 ++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/src/api/users/relationships.rs b/src/api/users/relationships.rs index a678da9..bab9a58 100644 --- a/src/api/users/relationships.rs +++ b/src/api/users/relationships.rs @@ -1,11 +1,11 @@ -use reqwest::Client; +use reqwest::{Client, RequestBuilder}; use serde_json::to_string; use crate::{ api::{deserialize_response, handle_request_as_option}, errors::ChorusLibError, instance::UserMeta, - types, + types::{self, CreateUserRelationshipSchema, RelationshipType}, }; impl UserMeta { @@ -73,4 +73,61 @@ impl UserMeta { let request = Client::new().post(url).bearer_auth(self.token()).body(body); handle_request_as_option(request, self, crate::api::limits::LimitType::Global).await } + + /// Modifies the relationship between the authenticated user and the specified user. + /// + /// # Arguments + /// + /// * `user_id` - A string slice that holds the ID of the user to modify the relationship with. + /// * `relationship_type` - A [`RelationshipType`] enum that specifies the type of relationship to modify. + /// * [`RelationshipType::None`]: Removes the relationship between the two users. + /// * [`RelationshipType::Friends`] | [`RelationshipType::Incoming`] | [`RelationshipType::Outgoing`]: + /// Either accepts an incoming friend request, or sends a new friend request, if there is no + /// incoming friend request from the specified `user_id`. + /// * [`RelationshipType::Blocked`]: Blocks the specified user_id. + /// + /// # Returns + /// This function returns an [`Option`] that holds a [`ChorusLibError`] if the request fails. + pub async fn modify_user_relationship( + &mut self, + user_id: &str, + relationship_type: RelationshipType, + ) -> Option { + let belongs_to = self.belongs_to.borrow(); + let api_url = belongs_to.urls.api.clone(); + drop(belongs_to); + match relationship_type { + RelationshipType::None => { + let request = Client::new() + .delete(format!("{}/users/@me/relationships/{}/", api_url, user_id)) + .bearer_auth(self.token()); + handle_request_as_option(request, self, crate::api::limits::LimitType::Global).await + } + RelationshipType::Friends | RelationshipType::Incoming | RelationshipType::Outgoing => { + let body = CreateUserRelationshipSchema { + relationship_type: None, // Selecting 'None' here will accept an incoming FR or send a new FR. + from_friend_suggestion: None, + friend_token: None, + }; + let request = Client::new() + .put(format!("{}/users/@me/relationships/{}/", api_url, user_id)) + .bearer_auth(self.token()) + .body(to_string(&body).unwrap()); + handle_request_as_option(request, self, crate::api::limits::LimitType::Global).await + } + RelationshipType::Blocked => { + let body = CreateUserRelationshipSchema { + relationship_type: Some(RelationshipType::Blocked), + from_friend_suggestion: None, + friend_token: None, + }; + let request = Client::new() + .put(format!("{}/users/@me/relationships/{}/", api_url, user_id)) + .bearer_auth(self.token()) + .body(to_string(&body).unwrap()); + handle_request_as_option(request, self, crate::api::limits::LimitType::Global).await + } + RelationshipType::Suggestion | RelationshipType::Implicit => None, + } + } } From 6d8e0aaa396ef24edf69f0fc635911cc7c1b5698 Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Sun, 18 Jun 2023 23:04:25 +0200 Subject: [PATCH 13/34] Remove unneccessary imports --- src/api/users/relationships.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/users/relationships.rs b/src/api/users/relationships.rs index bab9a58..7ad76fe 100644 --- a/src/api/users/relationships.rs +++ b/src/api/users/relationships.rs @@ -1,4 +1,4 @@ -use reqwest::{Client, RequestBuilder}; +use reqwest::Client; use serde_json::to_string; use crate::{ From a05241dd8e30d79e874925c68c43f23bbbba44b5 Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Sun, 18 Jun 2023 23:10:29 +0200 Subject: [PATCH 14/34] Add remove_relationship --- src/api/users/relationships.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/api/users/relationships.rs b/src/api/users/relationships.rs index 7ad76fe..65c044e 100644 --- a/src/api/users/relationships.rs +++ b/src/api/users/relationships.rs @@ -130,4 +130,24 @@ impl UserMeta { RelationshipType::Suggestion | RelationshipType::Implicit => None, } } + + /// Removes the relationship between the authenticated user and the specified user. + /// + /// # Arguments + /// + /// * `user_id` - A string slice that holds the ID of the user to remove the relationship with. + /// + /// # Returns + /// This function returns an [`Option`] that holds a [`ChorusLibError`] if the request fails. + pub async fn remove_relationship(&mut self, user_id: &str) -> Option { + let belongs_to = self.belongs_to.borrow(); + let url = format!( + "{}/users/@me/relationships/{}/", + belongs_to.urls.get_api(), + user_id + ); + drop(belongs_to); + let request = Client::new().post(url).bearer_auth(self.token()); + handle_request_as_option(request, self, crate::api::limits::LimitType::Global).await + } } From 9cc7243c98a5c4edd3d8d16f5b15c548fe26d66d Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Sun, 18 Jun 2023 23:24:10 +0200 Subject: [PATCH 15/34] Mark Friend Requests and Blocking as completed --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 82ac173..b239410 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,8 @@ ### User Management - [ ] [User profile customization](https://github.com/polyphony-chat/chorus/issues/41) - [x] Gettings users and user profiles -- [ ] [Friend requests](https://github.com/polyphony-chat/chorus/issues/92) -- [ ] [Blocking users](https://github.com/polyphony-chat/chorus/issues/92) +- [x] [Friend requests](https://github.com/polyphony-chat/chorus/issues/92) +- [x] [Blocking users](https://github.com/polyphony-chat/chorus/issues/92) - [ ] User presence (online, offline, idle, etc.) - [ ] User status (custom status, etc.) - [x] Account deletion From 37de1d7b0619cfde5640126159ad06f6e8f3da00 Mon Sep 17 00:00:00 2001 From: Vincent Junge Date: Mon, 19 Jun 2023 10:27:32 +0200 Subject: [PATCH 16/34] Fix various Clippy lints --- examples/gateway_observers.rs | 8 +- examples/gateway_simple.rs | 7 +- src/api/channels/channels.rs | 6 +- src/api/common.rs | 6 +- src/api/policies/instance/limits.rs | 80 ++++++++--------- src/api/users/users.rs | 19 ++--- src/gateway.rs | 85 +++++++------------ src/instance.rs | 13 +-- src/lib.rs | 2 + src/limit.rs | 21 ++--- .../config/types/general_configuration.rs | 24 ------ src/types/config/types/guild_configuration.rs | 1 + src/types/entities/application.rs | 2 +- src/types/entities/role.rs | 3 +- src/types/events/identify.rs | 44 +++++----- src/types/events/presence.rs | 10 +-- src/types/schema/auth.rs | 67 +++++++++------ src/types/schema/message.rs | 1 - src/types/schema/mod.rs | 30 ++----- tests/auth.rs | 19 ++--- tests/common/mod.rs | 19 ++--- tests/guild.rs | 7 +- tests/member.rs | 6 +- tests/relationships.rs | 36 +++----- tests/roles.rs | 5 +- 25 files changed, 203 insertions(+), 318 deletions(-) diff --git a/examples/gateway_observers.rs b/examples/gateway_observers.rs index 166b84a..6e71751 100644 --- a/examples/gateway_observers.rs +++ b/examples/gateway_observers.rs @@ -3,8 +3,8 @@ use chorus::{ gateway::{Gateway, Observer}, types::{GatewayIdentifyPayload, GatewayReady}, }; -use std::sync::Arc; -use tokio::{self, sync::Mutex}; +use std::{sync::Arc, time::Duration}; +use tokio::{self, sync::Mutex, time::sleep}; // This example creates a simple gateway connection and a basic observer struct @@ -54,5 +54,7 @@ async fn main() { gateway.send_identify(identify).await; // Do something on the main thread so we don't quit - loop {} + loop { + sleep(Duration::MAX).await; + } } diff --git a/examples/gateway_simple.rs b/examples/gateway_simple.rs index c346839..26e8416 100644 --- a/examples/gateway_simple.rs +++ b/examples/gateway_simple.rs @@ -1,4 +1,7 @@ +use std::time::Duration; + use chorus::{self, gateway::Gateway, types::GatewayIdentifyPayload}; +use tokio::time::sleep; /// This example creates a simple gateway connection and a session with an Identify event #[tokio::main] @@ -26,5 +29,7 @@ async fn main() { gateway.send_identify(identify).await; // Do something on the main thread so we don't quit - loop {} + loop { + sleep(Duration::MAX).await; + } } diff --git a/src/api/channels/channels.rs b/src/api/channels/channels.rs index 32d9c3a..391f5f1 100644 --- a/src/api/channels/channels.rs +++ b/src/api/channels/channels.rs @@ -57,11 +57,7 @@ impl Channel { drop(belongs_to); let response = common::handle_request(request, user, crate::api::limits::LimitType::Channel).await; - if response.is_err() { - return Some(response.err().unwrap()); - } else { - return None; - } + response.err() } /// Modifies a channel. diff --git a/src/api/common.rs b/src/api/common.rs index 0ca9063..60fd263 100644 --- a/src/api/common.rs +++ b/src/api/common.rs @@ -13,17 +13,13 @@ pub async fn handle_request( limit_type: LimitType, ) -> Result { let mut belongs_to = user.belongs_to.borrow_mut(); - match LimitedRequester::send_request( + LimitedRequester::send_request( request, limit_type, &mut belongs_to.limits, &mut user.limits, ) .await - { - Ok(response) => return Ok(response), - Err(e) => return Err(e), - } } /// Sends a request to wherever it needs to go. Returns [`None`] on success and diff --git a/src/api/policies/instance/limits.rs b/src/api/policies/instance/limits.rs index 98e0fc8..4ab17f5 100644 --- a/src/api/policies/instance/limits.rs +++ b/src/api/policies/instance/limits.rs @@ -195,31 +195,31 @@ pub mod limits { pub fn get_limit_ref(&self, limit_type: &LimitType) -> &Limit { match limit_type { - &LimitType::AbsoluteMessage => self.limit_absolute_messages, - &LimitType::AbsoluteRegister => self.limit_absolute_register, - &LimitType::AuthLogin => self.limit_auth_login, - &LimitType::AuthRegister => self.limit_auth_register, - &LimitType::Channel => self.limit_channel, - &LimitType::Error => self.limit_error, - &LimitType::Global => self.limit_global, - &LimitType::Guild => self.limit_guild, - &LimitType::Ip => self.limit_ip, - &LimitType::Webhook => self.limit_webhook, + LimitType::AbsoluteMessage => self.limit_absolute_messages, + LimitType::AbsoluteRegister => self.limit_absolute_register, + LimitType::AuthLogin => self.limit_auth_login, + LimitType::AuthRegister => self.limit_auth_register, + LimitType::Channel => self.limit_channel, + LimitType::Error => self.limit_error, + LimitType::Global => self.limit_global, + LimitType::Guild => self.limit_guild, + LimitType::Ip => self.limit_ip, + LimitType::Webhook => self.limit_webhook, } } pub fn get_limit_mut_ref(&mut self, limit_type: &LimitType) -> &mut Limit { match limit_type { - &LimitType::AbsoluteMessage => self.limit_absolute_messages, - &LimitType::AbsoluteRegister => self.limit_absolute_register, - &LimitType::AuthLogin => self.limit_auth_login, - &LimitType::AuthRegister => self.limit_auth_register, - &LimitType::Channel => self.limit_channel, - &LimitType::Error => self.limit_error, - &LimitType::Global => self.limit_global, - &LimitType::Guild => self.limit_guild, - &LimitType::Ip => self.limit_ip, - &LimitType::Webhook => self.limit_webhook, + LimitType::AbsoluteMessage => self.limit_absolute_messages, + LimitType::AbsoluteRegister => self.limit_absolute_register, + LimitType::AuthLogin => self.limit_auth_login, + LimitType::AuthRegister => self.limit_auth_register, + LimitType::Channel => self.limit_channel, + LimitType::Error => self.limit_error, + LimitType::Global => self.limit_global, + LimitType::Guild => self.limit_guild, + LimitType::Ip => self.limit_ip, + LimitType::Webhook => self.limit_webhook, } } } @@ -256,31 +256,31 @@ pub mod limits { pub fn get_limit_ref(&self, limit_type: &LimitType) -> &Limit { match limit_type { - &LimitType::AbsoluteMessage => &self.limit_absolute_messages, - &LimitType::AbsoluteRegister => &self.limit_absolute_register, - &LimitType::AuthLogin => &self.limit_auth_login, - &LimitType::AuthRegister => &self.limit_auth_register, - &LimitType::Channel => &self.limit_channel, - &LimitType::Error => &self.limit_error, - &LimitType::Global => &self.limit_global, - &LimitType::Guild => &self.limit_guild, - &LimitType::Ip => &self.limit_ip, - &LimitType::Webhook => &self.limit_webhook, + LimitType::AbsoluteMessage => &self.limit_absolute_messages, + LimitType::AbsoluteRegister => &self.limit_absolute_register, + LimitType::AuthLogin => &self.limit_auth_login, + LimitType::AuthRegister => &self.limit_auth_register, + LimitType::Channel => &self.limit_channel, + LimitType::Error => &self.limit_error, + LimitType::Global => &self.limit_global, + LimitType::Guild => &self.limit_guild, + LimitType::Ip => &self.limit_ip, + LimitType::Webhook => &self.limit_webhook, } } pub fn get_limit_mut_ref(&mut self, limit_type: &LimitType) -> &mut Limit { match limit_type { - &LimitType::AbsoluteMessage => &mut self.limit_absolute_messages, - &LimitType::AbsoluteRegister => &mut self.limit_absolute_register, - &LimitType::AuthLogin => &mut self.limit_auth_login, - &LimitType::AuthRegister => &mut self.limit_auth_register, - &LimitType::Channel => &mut self.limit_channel, - &LimitType::Error => &mut self.limit_error, - &LimitType::Global => &mut self.limit_global, - &LimitType::Guild => &mut self.limit_guild, - &LimitType::Ip => &mut self.limit_ip, - &LimitType::Webhook => &mut self.limit_webhook, + LimitType::AbsoluteMessage => &mut self.limit_absolute_messages, + LimitType::AbsoluteRegister => &mut self.limit_absolute_register, + LimitType::AuthLogin => &mut self.limit_auth_login, + LimitType::AuthRegister => &mut self.limit_auth_register, + LimitType::Channel => &mut self.limit_channel, + LimitType::Error => &mut self.limit_error, + LimitType::Global => &mut self.limit_global, + LimitType::Guild => &mut self.limit_guild, + LimitType::Ip => &mut self.limit_ip, + LimitType::Webhook => &mut self.limit_webhook, } } diff --git a/src/api/users/users.rs b/src/api/users/users.rs index 166bdd8..8423e01 100644 --- a/src/api/users/users.rs +++ b/src/api/users/users.rs @@ -54,7 +54,7 @@ impl UserMeta { let request = Client::new() .patch(format!( "{}/users/@me/", - self.belongs_to.borrow_mut().urls.get_api() + self.belongs_to.borrow().urls.get_api() )) .body(to_string(&modify_schema).unwrap()) .bearer_auth(self.token()); @@ -103,12 +103,11 @@ impl User { limits_instance: &mut Limits, id: Option<&String>, ) -> Result { - let url: String; - if id.is_none() { - url = format!("{}/users/@me/", url_api); + let url = if id.is_none() { + format!("{}/users/@me/", url_api) } else { - url = format!("{}/users/{}", url_api, id.unwrap()); - } + format!("{}/users/{}", url_api, id.unwrap()) + }; let request = reqwest::Client::new().get(url).bearer_auth(token); let mut cloned_limits = limits_instance.clone(); match LimitedRequester::send_request( @@ -166,12 +165,6 @@ impl Instance { token: String, id: Option<&String>, ) -> Result { - User::_get( - &token, - &self.urls.get_api().to_string(), - &mut self.limits, - id, - ) - .await + User::_get(&token, self.urls.get_api(), &mut self.limits, id).await } } diff --git a/src/gateway.rs b/src/gateway.rs index a765702..28ed702 100644 --- a/src/gateway.rs +++ b/src/gateway.rs @@ -94,60 +94,36 @@ impl GatewayMessage { let content = self.message.to_string(); // Some error strings have dots on the end, which we don't care about - let processed_content = content.clone().to_lowercase().replace(".", ""); + let processed_content = content.to_lowercase().replace('.', ""); match processed_content.as_str() { - "unknown error" | "4000" => { - return Some(GatewayError::UnknownError); - } - "unknown opcode" | "4001" => { - return Some(GatewayError::UnknownOpcodeError); - } + "unknown error" | "4000" => Some(GatewayError::UnknownError), + "unknown opcode" | "4001" => Some(GatewayError::UnknownOpcodeError), "decode error" | "error while decoding payload" | "4002" => { - return Some(GatewayError::DecodeError); - } - "not authenticated" | "4003" => { - return Some(GatewayError::NotAuthenticatedError); - } - "authentication failed" | "4004" => { - return Some(GatewayError::AuthenticationFailedError); - } - "already authenticated" | "4005" => { - return Some(GatewayError::AlreadyAuthenticatedError); - } - "invalid seq" | "4007" => { - return Some(GatewayError::InvalidSequenceNumberError); - } - "rate limited" | "4008" => { - return Some(GatewayError::RateLimitedError); - } - "session timed out" | "4009" => { - return Some(GatewayError::SessionTimedOutError); - } - "invalid shard" | "4010" => { - return Some(GatewayError::InvalidShardError); - } - "sharding required" | "4011" => { - return Some(GatewayError::ShardingRequiredError); - } - "invalid api version" | "4012" => { - return Some(GatewayError::InvalidAPIVersionError); + Some(GatewayError::DecodeError) } + "not authenticated" | "4003" => Some(GatewayError::NotAuthenticatedError), + "authentication failed" | "4004" => Some(GatewayError::AuthenticationFailedError), + "already authenticated" | "4005" => Some(GatewayError::AlreadyAuthenticatedError), + "invalid seq" | "4007" => Some(GatewayError::InvalidSequenceNumberError), + "rate limited" | "4008" => Some(GatewayError::RateLimitedError), + "session timed out" | "4009" => Some(GatewayError::SessionTimedOutError), + "invalid shard" | "4010" => Some(GatewayError::InvalidShardError), + "sharding required" | "4011" => Some(GatewayError::ShardingRequiredError), + "invalid api version" | "4012" => Some(GatewayError::InvalidAPIVersionError), "invalid intent(s)" | "invalid intent" | "4013" => { - return Some(GatewayError::InvalidIntentsError); + Some(GatewayError::InvalidIntentsError) } "disallowed intent(s)" | "disallowed intents" | "4014" => { - return Some(GatewayError::DisallowedIntentsError); - } - _ => { - return None; + Some(GatewayError::DisallowedIntentsError) } + _ => None, } } /// Returns whether or not the message is an error pub fn is_error(&self) -> bool { - return self.error().is_some(); + self.error().is_some() } /// Parses the message as a payload; @@ -168,7 +144,7 @@ impl GatewayMessage { /// Returns whether or not the message is empty pub fn is_empty(&self) -> bool { - return self.message.is_empty(); + self.message.is_empty() } } @@ -308,6 +284,7 @@ pub struct Gateway { } impl Gateway { + #[allow(clippy::new_ret_no_self)] pub async fn new(websocket_url: String) -> Result { let (websocket_stream, _) = match connect_async_tls_with_config( &websocket_url, @@ -371,13 +348,13 @@ impl Gateway { gateway.gateway_listen_task().await; }); - return Ok(GatewayHandle { + Ok(GatewayHandle { url: websocket_url.clone(), events: shared_events, websocket_send: shared_websocket_send.clone(), handle, kill_send: kill_send.clone(), - }); + }) } /// The main gateway listener task; @@ -388,14 +365,10 @@ impl Gateway { let msg = self.websocket_receive.next().await; // This if chain can be much better but if let is unstable on stable rust - if msg.as_ref().is_some() { - if msg.as_ref().unwrap().is_ok() { - let msg_unwrapped = msg.unwrap().unwrap(); - self.handle_message(GatewayMessage::from_tungstenite_message(msg_unwrapped)) - .await; - - continue; - } + if let Some(Ok(message)) = msg { + self.handle_message(GatewayMessage::from_tungstenite_message(message)) + .await; + continue; } // We couldn't receive the next message or it was an error, something is wrong with the websocket, close @@ -423,7 +396,7 @@ impl Gateway { } event.update_data(data_deserialize_result.unwrap()).await; - return Ok(()); + Ok(()) } /// This handles a message as a websocket event and updates its events along with the events' observers @@ -1773,7 +1746,7 @@ impl GatewayEvent { // The usage of the debug format to compare the generic T of observers is quite stupid, but the only thing to compare between them is T and if T == T they are the same // anddd there is no way to do that without using format self.observers - .retain(|obs| !(format!("{:?}", obs) == format!("{:?}", &observable))); + .retain(|obs| format!("{:?}", obs) != format!("{:?}", &observable)); self.is_observed = !self.observers.is_empty(); } @@ -1998,12 +1971,12 @@ mod example { let arc_mut_second_consumer = Arc::new(Mutex::new(second_consumer)); match event.subscribe(arc_mut_second_consumer.clone()).err() { - None => assert!(false), + None => panic!(), Some(err) => println!("You cannot subscribe twice: {}", err), } event.unsubscribe(arc_mut_consumer.clone()); - event.subscribe(arc_mut_second_consumer.clone()).unwrap(); + event.subscribe(arc_mut_second_consumer).unwrap(); } } diff --git a/src/instance.rs b/src/instance.rs index 42b7298..cc29c99 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -29,17 +29,8 @@ impl Instance { pub async fn new(urls: URLBundle) -> Result { let mut instance = Instance { urls: urls.clone(), - instance_info: GeneralConfiguration::new( - // This is okay, because the instance_info will be overwritten by the instance_policies_schema() function. - "".to_string(), - None, - None, - None, - None, - None, - None, - None, - ), + // Will be overwritten in the next step + instance_info: GeneralConfiguration::default(), limits: Limits::check_limits(urls.api).await, }; instance.instance_info = match instance.general_configuration_schema().await { diff --git a/src/lib.rs b/src/lib.rs index 2e25d3d..06cbb81 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![allow(clippy::module_inception)] + use url::{ParseError, Url}; #[cfg(feature = "client")] diff --git a/src/limit.rs b/src/limit.rs index 8fa5767..2857449 100644 --- a/src/limit.rs +++ b/src/limit.rs @@ -82,13 +82,11 @@ impl LimitedRequester { ); if !response.status().is_success() { match response.status().as_u16() { - 401 => return Err(ChorusLibError::TokenExpired), - 403 => return Err(ChorusLibError::TokenExpired), - _ => { - return Err(ChorusLibError::ReceivedErrorCodeError { - error_code: response.status().as_str().to_string(), - }); - } + 401 => Err(ChorusLibError::TokenExpired), + 403 => Err(ChorusLibError::TokenExpired), + _ => Err(ChorusLibError::ReceivedErrorCodeError { + error_code: response.status().as_str().to_string(), + }), } } else { Ok(response) @@ -286,14 +284,7 @@ mod rate_limit { .await, ); } - if request.is_some() { - match request.unwrap() { - Ok(_) => assert!(false), - Err(_) => assert!(true), - } - } else { - assert!(false) - } + assert!(matches!(request, Some(Err(_)))); } #[tokio::test] diff --git a/src/types/config/types/general_configuration.rs b/src/types/config/types/general_configuration.rs index a3c8f65..07444b0 100644 --- a/src/types/config/types/general_configuration.rs +++ b/src/types/config/types/general_configuration.rs @@ -31,27 +31,3 @@ impl Default for GeneralConfiguration { } } } - -impl GeneralConfiguration { - pub fn new( - instance_name: String, - instance_description: Option, - front_page: Option, - tos_page: Option, - correspondence_email: Option, - correspondence_user_id: Option, - image: Option, - instance_id: Option, - ) -> Self { - Self { - instance_name, - instance_description, - front_page, - tos_page, - correspondence_email, - correspondence_user_id, - image, - instance_id, - } - } -} diff --git a/src/types/config/types/guild_configuration.rs b/src/types/config/types/guild_configuration.rs index 4fedf51..a854460 100644 --- a/src/types/config/types/guild_configuration.rs +++ b/src/types/config/types/guild_configuration.rs @@ -1,4 +1,5 @@ use std::fmt::{Display, Formatter}; +#[cfg(feature = "sqlx")] use std::io::Write; use std::ops::{Deref, DerefMut}; use std::str::FromStr; diff --git a/src/types/entities/application.rs b/src/types/entities/application.rs index e5c4a01..6cac20b 100644 --- a/src/types/entities/application.rs +++ b/src/types/entities/application.rs @@ -1,4 +1,4 @@ -use bitflags::{bitflags, Flags}; +use bitflags::bitflags; use serde::{Deserialize, Serialize}; use serde_json::Value; use serde_repr::{Deserialize_repr, Serialize_repr}; diff --git a/src/types/entities/role.rs b/src/types/entities/role.rs index 7f527be..1719d28 100644 --- a/src/types/entities/role.rs +++ b/src/types/entities/role.rs @@ -108,6 +108,7 @@ impl PermissionFlags { self.contains(permission) || self.contains(PermissionFlags::ADMINISTRATOR) } + #[allow(clippy::inherent_to_string)] pub fn to_string(&self) -> String { self.bits().to_string() } @@ -129,7 +130,7 @@ impl PermissionFlags { pub fn from_vec(flags: Vec) -> String { let mut permissions: PermissionFlags = Default::default(); for flag in flags.iter() { - permissions = permissions | flag.clone(); + permissions |= flag.clone(); } permissions.to_string() } diff --git a/src/types/events/identify.rs b/src/types/events/identify.rs index dcd3a8a..35e1f78 100644 --- a/src/types/events/identify.rs +++ b/src/types/events/identify.rs @@ -51,16 +51,18 @@ impl GatewayIdentifyPayload { impl GatewayIdentifyPayload { /// Creates an identify payload with the same default capabilities as the official client pub fn default_w_client_capabilities() -> Self { - let mut def = Self::default(); - def.capabilities = Some(8189); // Default capabilities for a client - def + Self { + capabilities: Some(8189), // Default capabilities for a client + ..Self::default() + } } /// Creates an identify payload with all possible capabilities pub fn default_w_all_capabilities() -> Self { - let mut def = Self::default(); - def.capabilities = Some(i32::MAX); // Since discord uses bitwise for capabilities, this has almost every bit as 1, so all capabilities - def + Self { + capabilities: Some(i32::MAX), // Since discord uses bitwise for capabilities, this has almost every bit as 1, so all capabilities + ..Self::default() + } } } @@ -148,22 +150,18 @@ impl GatewayIdentifyConnectionProps { /// Returns the most common connection props so we can't be tracked pub fn common() -> Self { - let mut default = Self::minimal(); - - // See https://www.useragents.me/#most-common-desktop-useragents - // 25% of the web - //default.browser_user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36".to_string(); - default.browser = String::from("Chrome"); - default.browser_version = String::from("113.0.0.0"); - - default.system_locale = String::from("en-US"); - - default.os = String::from("Windows"); - default.os_version = Some(String::from("10")); - - default.client_build_number = 199933; - default.release_channel = String::from("stable"); - - return default; + Self { + // See https://www.useragents.me/#most-common-desktop-useragents + // 25% of the web + //default.browser_user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36".to_string(); + browser: String::from("Chrome"), + browser_version: String::from("113.0.0.0"), + system_locale: String::from("en-US"), + os: String::from("Windows"), + os_version: Some(String::from("10")), + client_build_number: 199933, + release_channel: String::from("stable"), + ..Self::minimal() + } } } diff --git a/src/types/events/presence.rs b/src/types/events/presence.rs index 93afbf5..ad06954 100644 --- a/src/types/events/presence.rs +++ b/src/types/events/presence.rs @@ -1,5 +1,5 @@ use crate::types::{events::WebSocketEvent, UserStatus}; -use crate::types::{Activity, PublicUser, Snowflake}; +use crate::types::{Activity, ClientStatusObject, PublicUser, Snowflake}; use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize, Serialize, Default, Clone)] @@ -26,12 +26,4 @@ pub struct PresenceUpdate { pub client_status: ClientStatusObject, } -#[derive(Debug, Deserialize, Serialize, Default, Clone)] -/// See https://discord.com/developers/docs/topics/gateway-events#client-status-object -pub struct ClientStatusObject { - pub desktop: Option, - pub mobile: Option, - pub web: Option, -} - impl WebSocketEvent for PresenceUpdate {} diff --git a/src/types/schema/auth.rs b/src/types/schema/auth.rs index fc7a1ad..8b8a601 100644 --- a/src/types/schema/auth.rs +++ b/src/types/schema/auth.rs @@ -116,9 +116,39 @@ pub struct RegisterSchema { promotional_email_opt_in: Option, } +pub struct RegisterSchemaOptions { + pub username: String, + pub password: Option, + pub consent: bool, + pub email: Option, + pub fingerprint: Option, + pub invite: Option, + pub date_of_birth: Option, + pub gift_code_sku_id: Option, + pub captcha_key: Option, + pub promotional_email_opt_in: Option, +} + impl RegisterSchema { + pub fn builder(username: impl Into, consent: bool) -> RegisterSchemaOptions { + RegisterSchemaOptions { + username: username.into(), + password: None, + consent, + email: None, + fingerprint: None, + invite: None, + date_of_birth: None, + gift_code_sku_id: None, + captcha_key: None, + promotional_email_opt_in: None, + } + } +} + +impl RegisterSchemaOptions { /** - Returns a new [`Result`]. + Create a new [`RegisterSchema`]. ## Arguments All but "String::username" and "bool::consent" are optional. @@ -129,47 +159,36 @@ impl RegisterSchema { These constraints have been defined [in the Spacebar-API](https://docs.spacebar.chat/routes/) */ - pub fn new( - username: String, - password: Option, - consent: bool, - email: Option, - fingerprint: Option, - invite: Option, - date_of_birth: Option, - gift_code_sku_id: Option, - captcha_key: Option, - promotional_email_opt_in: Option, - ) -> Result { - let username = AuthUsername::new(username)?.username; + pub fn build(self) -> Result { + let username = AuthUsername::new(self.username)?.username; - let email = if let Some(email) = email { + let email = if let Some(email) = self.email { Some(AuthEmail::new(email)?.email) } else { None }; - let password = if let Some(password) = password { + let password = if let Some(password) = self.password { Some(AuthPassword::new(password)?.password) } else { None }; - if !consent { + if !self.consent { return Err(FieldFormatError::ConsentError); } Ok(RegisterSchema { username, password, - consent, + consent: self.consent, email, - fingerprint, - invite, - date_of_birth, - gift_code_sku_id, - captcha_key, - promotional_email_opt_in, + fingerprint: self.fingerprint, + invite: self.invite, + date_of_birth: self.date_of_birth, + gift_code_sku_id: self.gift_code_sku_id, + captcha_key: self.captcha_key, + promotional_email_opt_in: self.promotional_email_opt_in, }) } } diff --git a/src/types/schema/message.rs b/src/types/schema/message.rs index 7ff27f1..20523b4 100644 --- a/src/types/schema/message.rs +++ b/src/types/schema/message.rs @@ -20,7 +20,6 @@ pub struct MessageSendSchema { pub attachments: Option>, } -// make a new() method for MessageSendSchema impl MessageSendSchema { pub fn new( message_type: Option, diff --git a/src/types/schema/mod.rs b/src/types/schema/mod.rs index b82aea2..1069428 100644 --- a/src/types/schema/mod.rs +++ b/src/types/schema/mod.rs @@ -65,18 +65,7 @@ mod schemas_tests { #[test] fn consent_false() { assert_eq!( - RegisterSchema::new( - "Test".to_string(), - None, - false, - None, - None, - None, - None, - None, - None, - None, - ), + RegisterSchema::builder("Test", false).build(), Err(FieldFormatError::ConsentError) ); } @@ -91,18 +80,11 @@ mod schemas_tests { #[test] fn valid_email() { - let reg = RegisterSchema::new( - "Testy".to_string(), - None, - true, - Some("me@mail.de".to_string()), - None, - None, - None, - None, - None, - None, - ); + let reg = RegisterSchemaOptions { + email: Some("me@mail.de".to_string()), + ..RegisterSchema::builder("Testy", true) + } + .build(); assert_ne!(reg, Err(FieldFormatError::EmailError)); } } diff --git a/tests/auth.rs b/tests/auth.rs index 24abefc..6972ace 100644 --- a/tests/auth.rs +++ b/tests/auth.rs @@ -1,22 +1,15 @@ -use chorus::types; +use chorus::types::{RegisterSchema, RegisterSchemaOptions}; mod common; #[tokio::test] async fn test_registration() { let mut bundle = common::setup().await; - let reg = types::RegisterSchema::new( - "Hiiii".to_string(), - None, - true, - None, - None, - None, - Some("2000-01-01".to_string()), - None, - None, - None, - ) + let reg = RegisterSchemaOptions { + date_of_birth: Some("2000-01-01".to_string()), + ..RegisterSchema::builder("Hiiii", true) + } + .build() .unwrap(); bundle.instance.register_account(®).await.unwrap(); common::teardown(bundle).await; diff --git a/tests/common/mod.rs b/tests/common/mod.rs index c58c79a..5a29a0b 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -2,7 +2,7 @@ use chorus::{ instance::{Instance, UserMeta}, types::{ Channel, ChannelCreateSchema, Guild, GuildCreateSchema, RegisterSchema, - RoleCreateModifySchema, RoleObject, + RegisterSchemaOptions, RoleCreateModifySchema, RoleObject, }, URLBundle, }; @@ -26,18 +26,11 @@ pub async fn setup() -> TestBundle { ); let mut instance = Instance::new(urls.clone()).await.unwrap(); // Requires the existance of the below user. - let reg = RegisterSchema::new( - "integrationtestuser".to_string(), - None, - true, - None, - None, - None, - Some("2000-01-01".to_string()), - None, - None, - None, - ) + let reg = RegisterSchemaOptions { + date_of_birth: Some("2000-01-01".to_string()), + ..RegisterSchema::builder("integrationtestuser", true) + } + .build() .unwrap(); let guild_create_schema = GuildCreateSchema { name: Some("Test-Guild!".to_string()), diff --git a/tests/guild.rs b/tests/guild.rs index 3f7037c..903b1eb 100644 --- a/tests/guild.rs +++ b/tests/guild.rs @@ -20,10 +20,9 @@ async fn guild_creation_deletion() { .await .unwrap(); - match Guild::delete(&mut bundle.user, &guild.id.to_string()).await { - None => assert!(true), - Some(_) => assert!(false), - } + assert!(Guild::delete(&mut bundle.user, &guild.id.to_string()) + .await + .is_none()); common::teardown(bundle).await } diff --git a/tests/member.rs b/tests/member.rs index c6fc6e3..4ed676e 100644 --- a/tests/member.rs +++ b/tests/member.rs @@ -18,7 +18,7 @@ async fn add_remove_role() { } } if !role_found { - assert!(false) + panic!() } chorus::types::GuildMember::remove_role(&mut bundle.user, guild_id, user_id, role_id).await; let member = chorus::types::GuildMember::get(&mut bundle.user, guild_id, user_id) @@ -28,11 +28,11 @@ async fn add_remove_role() { if role != role_id { role_found = false; } else { - assert!(false); + panic!(); } } if role_found { - assert!(false) + panic!() } common::teardown(bundle).await } diff --git a/tests/relationships.rs b/tests/relationships.rs index b575046..187f07e 100644 --- a/tests/relationships.rs +++ b/tests/relationships.rs @@ -1,21 +1,14 @@ -use chorus::types; +use chorus::types::{self, RegisterSchema, RegisterSchemaOptions}; mod common; #[tokio::test] async fn test_get_mutual_relationships() { - let register_schema = types::RegisterSchema::new( - "integrationtestuser2".to_string(), - None, - true, - None, - None, - None, - Some("2000-01-01".to_string()), - None, - None, - None, - ) + let register_schema = RegisterSchemaOptions { + date_of_birth: Some("2000-01-01".to_string()), + ..RegisterSchema::builder("integrationtestuser2", true) + } + .build() .unwrap(); let mut bundle = common::setup().await; @@ -37,18 +30,11 @@ async fn test_get_mutual_relationships() { #[tokio::test] async fn test_get_relationships() { - let register_schema = types::RegisterSchema::new( - "integrationtestuser2".to_string(), - None, - true, - None, - None, - None, - Some("2000-01-01".to_string()), - None, - None, - None, - ) + let register_schema = RegisterSchemaOptions { + date_of_birth: Some("2000-01-01".to_string()), + ..RegisterSchema::builder("integrationtestuser2", true) + } + .build() .unwrap(); let mut bundle = common::setup().await; diff --git a/tests/roles.rs b/tests/roles.rs index fd80de3..6cce1d3 100644 --- a/tests/roles.rs +++ b/tests/roles.rs @@ -25,10 +25,7 @@ async fn create_and_get_roles() { let expected = types::RoleObject::get_all(&mut bundle.user, &guild_id) .await .unwrap() - .unwrap() - .iter() - .nth(2) - .unwrap() + .unwrap()[2] .clone(); assert_eq!(role, expected); From cadf14ba6a338fd9823e7cc7d39eb823e696d368 Mon Sep 17 00:00:00 2001 From: Vincent Junge Date: Mon, 19 Jun 2023 14:21:27 +0200 Subject: [PATCH 17/34] Make MessageSendSchema fields public --- src/types/schema/message.rs | 46 ++++++++----------------------------- tests/message.rs | 33 ++++++++------------------ 2 files changed, 18 insertions(+), 61 deletions(-) diff --git a/src/types/schema/message.rs b/src/types/schema/message.rs index 20523b4..ade8e4d 100644 --- a/src/types/schema/message.rs +++ b/src/types/schema/message.rs @@ -8,42 +8,14 @@ use crate::types::entities::{ #[serde(rename_all = "snake_case")] pub struct MessageSendSchema { #[serde(rename = "type")] - message_type: Option, - content: Option, - nonce: Option, - tts: Option, - embeds: Option>, - allowed_mentions: Option, - message_reference: Option, - components: Option>, - sticker_ids: Option>, + pub message_type: Option, + pub content: Option, + pub nonce: Option, + pub tts: Option, + pub embeds: Option>, + pub allowed_mentions: Option, + pub message_reference: Option, + pub components: Option>, + pub sticker_ids: Option>, pub attachments: Option>, } - -impl MessageSendSchema { - pub fn new( - message_type: Option, - content: Option, - nonce: Option, - tts: Option, - embeds: Option>, - allowed_mentions: Option, - message_reference: Option, - components: Option>, - sticker_ids: Option>, - attachments: Option>, - ) -> MessageSendSchema { - MessageSendSchema { - message_type, - content, - nonce, - tts, - embeds, - allowed_mentions, - message_reference, - components, - sticker_ids, - attachments, - } - } -} diff --git a/tests/message.rs b/tests/message.rs index d614538..e92c35c 100644 --- a/tests/message.rs +++ b/tests/message.rs @@ -8,18 +8,10 @@ mod common; #[tokio::test] async fn send_message() { let mut bundle = common::setup().await; - let mut message = types::MessageSendSchema::new( - None, - Some("A Message!".to_string()), - None, - None, - None, - None, - None, - None, - None, - None, - ); + let mut message = types::MessageSendSchema { + content: Some("A Message!".to_string()), + ..Default::default() + }; let _ = bundle .user .send_message(&mut message, bundle.channel.id.to_string(), None) @@ -53,18 +45,11 @@ async fn send_message_attachment() { content: buffer, }; - let mut message = types::MessageSendSchema::new( - None, - Some("trans rights now".to_string()), - None, - None, - None, - None, - None, - None, - None, - Some(vec![attachment.clone()]), - ); + let mut message = types::MessageSendSchema { + content: Some("trans rights now".to_string()), + attachments: Some(vec![attachment.clone()]), + ..Default::default() + }; let vec_attach = vec![attachment.clone()]; let _arg = Some(&vec_attach); From b8e8a1c23ae69d247cc205dd414a4a2346a32be9 Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Mon, 19 Jun 2023 16:11:53 +0200 Subject: [PATCH 18/34] Fix build failing --- src/types/schema/message.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/schema/message.rs b/src/types/schema/message.rs index ade8e4d..e1abdf7 100644 --- a/src/types/schema/message.rs +++ b/src/types/schema/message.rs @@ -4,7 +4,7 @@ use crate::types::entities::{ AllowedMention, Component, Embed, MessageReference, PartialDiscordFileAttachment, }; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Default, Deserialize, Serialize)] #[serde(rename_all = "snake_case")] pub struct MessageSendSchema { #[serde(rename = "type")] From 36158548be8b63c769410c1ce145c996d950e6bd Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Mon, 19 Jun 2023 16:21:53 +0200 Subject: [PATCH 19/34] Rename build_and_test, add clippy actions --- .github/workflows/{rust.yml => build_and_test.yml} | 4 ++-- .github/workflows/clippy.yml | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) rename .github/workflows/{rust.yml => build_and_test.yml} (96%) create mode 100644 .github/workflows/clippy.yml diff --git a/.github/workflows/rust.yml b/.github/workflows/build_and_test.yml similarity index 96% rename from .github/workflows/rust.yml rename to .github/workflows/build_and_test.yml index 9b39ddf..4e98355 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/build_and_test.yml @@ -1,4 +1,4 @@ -name: Rust +name: Build and Test on: push: @@ -10,7 +10,7 @@ env: CARGO_TERM_COLOR: always jobs: - build_and_test: + rust: runs-on: ubuntu-latest diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml new file mode 100644 index 0000000..d190465 --- /dev/null +++ b/.github/workflows/clippy.yml @@ -0,0 +1,14 @@ +on: push +name: Clippy check + +# Make sure CI fails on all warnings, including Clippy lints +env: + RUSTFLAGS: "-Dwarnings" + +jobs: + clippy_check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Run Clippy + run: cargo clippy --all-targets --all-features \ No newline at end of file From 4fd3e3464a8975b533789b1ece2c2e0aedfd94c8 Mon Sep 17 00:00:00 2001 From: Flori <39242991+bitfl0wer@users.noreply.github.com> Date: Mon, 19 Jun 2023 16:28:43 +0200 Subject: [PATCH 20/34] Update badges --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4917884..318fc23 100644 --- a/README.md +++ b/README.md @@ -104,8 +104,10 @@ [Rust]: https://img.shields.io/badge/Rust-orange?style=plastic&logo=rust [Rust-url]: https://www.rust-lang.org/ -[build-shield]: https://img.shields.io/github/actions/workflow/status/polyphony-chat/chorus/rust.yml?style=flat -[build-url]: https://github.com/polyphony-chat/chorus/blob/main/.github/workflows/rust.yml +[build-shield]: https://img.shields.io/github/actions/workflow/status/polyphony-chat/chorus/build_and_test.yml?style=flat +[build-url]: https://github.com/polyphony-chat/chorus/blob/main/.github/workflows/build_and_test.yml +[clippy-shield]: https://img.shields.io/github/actions/workflow/status/polyphony-chat/chorus/clippy.yml?style=flat +[clippy-url]: https://github.com/polyphony-chat/chorus/blob/main/.github/workflows/clippy.yml [contributors-shield]: https://img.shields.io/github/contributors/polyphony-chat/chorus.svg?style=flat [contributors-url]: https://github.com/polyphony-chat/chorus/graphs/contributors [forks-shield]: https://img.shields.io/github/forks/polyphony-chat/chorus.svg?style=flat From b837508f41e0abb6c5cfcb1f06e58622e396a733 Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Mon, 19 Jun 2023 16:30:46 +0200 Subject: [PATCH 21/34] Change Clippy CI to only run on main pushes or PRs --- .github/workflows/clippy.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index d190465..ba12407 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -1,6 +1,12 @@ -on: push name: Clippy check +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + + # Make sure CI fails on all warnings, including Clippy lints env: RUSTFLAGS: "-Dwarnings" From de5fe8aebf2c7647899a818a080c9206697af89e Mon Sep 17 00:00:00 2001 From: Vincent Junge Date: Mon, 19 Jun 2023 10:13:48 +0200 Subject: [PATCH 22/34] Fix clippy::await_holding_refcell_ref false positives --- src/api/auth/register.rs | 2 +- src/api/channels/channels.rs | 12 ++---- src/api/channels/messages.rs | 4 +- src/api/channels/permissions.rs | 20 +++++----- src/api/channels/reactions.rs | 24 +++--------- src/api/common.rs | 3 +- src/api/guilds/guilds.rs | 16 ++++---- src/api/guilds/member.rs | 12 ++---- src/api/guilds/roles.rs | 65 +++++++++++++++------------------ src/api/users/relationships.rs | 26 ++++++------- src/api/users/users.rs | 7 ++-- 11 files changed, 75 insertions(+), 116 deletions(-) diff --git a/src/api/auth/register.rs b/src/api/auth/register.rs index 80485eb..0698704 100644 --- a/src/api/auth/register.rs +++ b/src/api/auth/register.rs @@ -63,7 +63,7 @@ impl Instance { UserMeta::get_settings(&token, &self.urls.get_api().to_string(), &mut self.limits) .await .unwrap(); - let user: UserMeta = UserMeta::new( + let user = UserMeta::new( Rc::new(RefCell::new(self.clone())), token.clone(), cloned_limits, diff --git a/src/api/channels/channels.rs b/src/api/channels/channels.rs index 391f5f1..7c4c8aa 100644 --- a/src/api/channels/channels.rs +++ b/src/api/channels/channels.rs @@ -10,9 +10,7 @@ use crate::{ impl Channel { pub async fn get(user: &mut UserMeta, channel_id: &str) -> Result { - let belongs_to = user.belongs_to.borrow_mut(); - let url = belongs_to.urls.get_api().to_string(); - drop(belongs_to); + let url = user.belongs_to.borrow_mut().urls.get_api().to_string(); let request = Client::new() .get(format!("{}/channels/{}/", url, channel_id)) .bearer_auth(user.token()); @@ -46,15 +44,13 @@ impl Channel { /// /// An `Option` that contains an `ChorusLibError` if an error occurred during the request, or `None` if the request was successful. pub async fn delete(self, user: &mut UserMeta) -> Option { - let belongs_to = user.belongs_to.borrow_mut(); let request = Client::new() .delete(format!( "{}/channels/{}/", - belongs_to.urls.get_api(), + user.belongs_to.borrow_mut().urls.get_api(), self.id.to_string() )) .bearer_auth(user.token()); - drop(belongs_to); let response = common::handle_request(request, user, crate::api::limits::LimitType::Channel).await; response.err() @@ -79,16 +75,14 @@ impl Channel { channel_id: &str, user: &mut UserMeta, ) -> Result { - let belongs_to = user.belongs_to.borrow(); let request = Client::new() .patch(format!( "{}/channels/{}/", - belongs_to.urls.get_api(), + user.belongs_to.borrow().urls.get_api(), channel_id )) .bearer_auth(user.token()) .body(to_string(&modify_data).unwrap()); - drop(belongs_to); let channel = common::deserialize_response::( request, user, diff --git a/src/api/channels/messages.rs b/src/api/channels/messages.rs index a20570f..281f8ff 100644 --- a/src/api/channels/messages.rs +++ b/src/api/channels/messages.rs @@ -25,9 +25,7 @@ impl Message { message: &mut MessageSendSchema, files: Option>, ) -> Result { - let belongs_to = user.belongs_to.borrow(); - let url_api = belongs_to.urls.get_api().to_string(); - drop(belongs_to); + let url_api = user.belongs_to.borrow().urls.get_api().to_string(); if files.is_none() { let request = Client::new() diff --git a/src/api/channels/permissions.rs b/src/api/channels/permissions.rs index 120117b..cd12f30 100644 --- a/src/api/channels/permissions.rs +++ b/src/api/channels/permissions.rs @@ -25,14 +25,14 @@ impl types::Channel { channel_id: &str, overwrite: PermissionOverwrite, ) -> Option { - let belongs_to = user.belongs_to.borrow_mut(); - let url = format!( - "{}/channels/{}/permissions/{}", - belongs_to.urls.get_api(), - channel_id, - overwrite.id - ); - drop(belongs_to); + let url = { + format!( + "{}/channels/{}/permissions/{}", + user.belongs_to.borrow_mut().urls.get_api(), + channel_id, + overwrite.id + ) + }; let body = match to_string(&overwrite) { Ok(string) => string, Err(e) => { @@ -66,14 +66,12 @@ impl types::Channel { channel_id: &str, overwrite_id: &str, ) -> Option { - let belongs_to = user.belongs_to.borrow_mut(); let url = format!( "{}/channels/{}/permissions/{}", - belongs_to.urls.get_api(), + user.belongs_to.borrow_mut().urls.get_api(), channel_id, overwrite_id ); - drop(belongs_to); let request = Client::new().delete(url).bearer_auth(user.token()); match handle_request(request, user, crate::api::limits::LimitType::Channel).await { Ok(_) => None, diff --git a/src/api/channels/reactions.rs b/src/api/channels/reactions.rs index ea00219..0f54002 100644 --- a/src/api/channels/reactions.rs +++ b/src/api/channels/reactions.rs @@ -31,14 +31,12 @@ impl ReactionMeta { See [https://discord.com/developers/docs/resources/channel#delete-all-reactions](https://discord.com/developers/docs/resources/channel#delete-all-reactions) */ pub async fn delete_all(&self, user: &mut UserMeta) -> Option { - let belongs_to = user.belongs_to.borrow(); let url = format!( "{}/channels/{}/messages/{}/reactions/", - belongs_to.urls.get_api(), + user.belongs_to.borrow().urls.get_api(), self.channel_id, self.message_id ); - drop(belongs_to); let request = Client::new().delete(url).bearer_auth(user.token()); match handle_request(request, user, crate::api::limits::LimitType::Channel).await { Ok(_) => None, @@ -64,15 +62,13 @@ impl ReactionMeta { See [https://discord.com/developers/docs/resources/channel#get-reactions](https://discord.com/developers/docs/resources/channel#get-reactions) */ pub async fn get(&self, emoji: &str, user: &mut UserMeta) -> Option { - let belongs_to = user.belongs_to.borrow(); let url = format!( "{}/channels/{}/messages/{}/reactions/{}/", - belongs_to.urls.get_api(), + user.belongs_to.borrow().urls.get_api(), self.channel_id, self.message_id, emoji ); - drop(belongs_to); let request = Client::new().get(url).bearer_auth(user.token()); match handle_request(request, user, crate::api::limits::LimitType::Channel).await { Ok(_) => None, @@ -100,15 +96,13 @@ impl ReactionMeta { See [https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji](https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji) */ pub async fn delete_emoji(&self, emoji: &str, user: &mut UserMeta) -> Option { - let belongs_to = user.belongs_to.borrow(); let url = format!( "{}/channels/{}/messages/{}/reactions/{}/", - belongs_to.urls.get_api(), + user.belongs_to.borrow().urls.get_api(), self.channel_id, self.message_id, emoji ); - drop(belongs_to); let request = Client::new().delete(url).bearer_auth(user.token()); match handle_request(request, user, crate::api::limits::LimitType::Channel).await { Ok(_) => None, @@ -140,15 +134,13 @@ impl ReactionMeta { See [https://discord.com/developers/docs/resources/channel#create-reaction](https://discord.com/developers/docs/resources/channel#create-reaction) */ pub async fn create(&self, emoji: &str, user: &mut UserMeta) -> Option { - let belongs_to = user.belongs_to.borrow(); let url = format!( "{}/channels/{}/messages/{}/reactions/{}/@me/", - belongs_to.urls.get_api(), + user.belongs_to.borrow().urls.get_api(), self.channel_id, self.message_id, emoji ); - drop(belongs_to); let request = Client::new().put(url).bearer_auth(user.token()); handle_request_as_option(request, user, crate::api::limits::LimitType::Channel).await } @@ -171,15 +163,13 @@ impl ReactionMeta { See [https://discord.com/developers/docs/resources/channel#delete-own-reaction](https://discord.com/developers/docs/resources/channel#delete-own-reaction) */ pub async fn remove(&self, emoji: &str, user: &mut UserMeta) -> Option { - let belongs_to = user.belongs_to.borrow(); let url = format!( "{}/channels/{}/messages/{}/reactions/{}/@me/", - belongs_to.urls.get_api(), + user.belongs_to.borrow().urls.get_api(), self.channel_id, self.message_id, emoji ); - drop(belongs_to); let request = Client::new().delete(url).bearer_auth(user.token()); handle_request_as_option(request, user, crate::api::limits::LimitType::Channel).await } @@ -210,16 +200,14 @@ impl ReactionMeta { emoji: &str, user: &mut UserMeta, ) -> Option { - let belongs_to = user.belongs_to.borrow(); let url = format!( "{}/channels/{}/messages/{}/reactions/{}/{}", - belongs_to.urls.get_api(), + user.belongs_to.borrow().urls.get_api(), self.channel_id, self.message_id, emoji, user_id ); - drop(belongs_to); let request = Client::new().delete(url).bearer_auth(user.token()); handle_request_as_option(request, user, crate::api::limits::LimitType::Channel).await } diff --git a/src/api/common.rs b/src/api/common.rs index 60fd263..cecec5a 100644 --- a/src/api/common.rs +++ b/src/api/common.rs @@ -12,11 +12,10 @@ pub async fn handle_request( user: &mut UserMeta, limit_type: LimitType, ) -> Result { - let mut belongs_to = user.belongs_to.borrow_mut(); LimitedRequester::send_request( request, limit_type, - &mut belongs_to.limits, + &mut user.belongs_to.borrow_mut().limits, &mut user.limits, ) .await diff --git a/src/api/guilds/guilds.rs b/src/api/guilds/guilds.rs index 8259c0e..6fea804 100644 --- a/src/api/guilds/guilds.rs +++ b/src/api/guilds/guilds.rs @@ -32,9 +32,7 @@ impl Guild { user: &mut UserMeta, guild_create_schema: GuildCreateSchema, ) -> Result { - let belongs_to = user.belongs_to.borrow(); - let url = format!("{}/guilds/", belongs_to.urls.get_api()); - drop(belongs_to); + let url = format!("{}/guilds/", user.belongs_to.borrow().urls.get_api()); let request = reqwest::Client::new() .post(url.clone()) .bearer_auth(user.token.clone()) @@ -67,9 +65,11 @@ impl Guild { /// } /// ``` pub async fn delete(user: &mut UserMeta, guild_id: &str) -> Option { - let belongs_to = user.belongs_to.borrow(); - let url = format!("{}/guilds/{}/delete/", belongs_to.urls.get_api(), guild_id); - drop(belongs_to); + let url = format!( + "{}/guilds/{}/delete/", + user.belongs_to.borrow().urls.get_api(), + guild_id + ); let request = reqwest::Client::new() .post(url.clone()) .bearer_auth(user.token.clone()); @@ -116,15 +116,13 @@ impl Guild { /// * `limits_instance` - A mutable reference to a `Limits` struct containing the instance's rate limits. /// pub async fn channels(&self, user: &mut UserMeta) -> Result, ChorusLibError> { - let belongs_to = user.belongs_to.borrow(); let request = Client::new() .get(format!( "{}/guilds/{}/channels/", - belongs_to.urls.get_api(), + user.belongs_to.borrow().urls.get_api(), self.id.to_string() )) .bearer_auth(user.token()); - drop(belongs_to); let result = handle_request(request, user, crate::api::limits::LimitType::Channel) .await .unwrap(); diff --git a/src/api/guilds/member.rs b/src/api/guilds/member.rs index 8a18178..4529073 100644 --- a/src/api/guilds/member.rs +++ b/src/api/guilds/member.rs @@ -24,14 +24,12 @@ impl types::GuildMember { guild_id: &str, member_id: &str, ) -> Result { - let belongs_to = user.belongs_to.borrow(); let url = format!( "{}/guilds/{}/members/{}/", - belongs_to.urls.get_api(), + user.belongs_to.borrow().urls.get_api(), guild_id, member_id ); - drop(belongs_to); let request = Client::new().get(url).bearer_auth(user.token()); deserialize_response::( request, @@ -59,15 +57,13 @@ impl types::GuildMember { member_id: &str, role_id: &str, ) -> Option { - let belongs_to = user.belongs_to.borrow(); let url = format!( "{}/guilds/{}/members/{}/roles/{}/", - belongs_to.urls.get_api(), + user.belongs_to.borrow().urls.get_api(), guild_id, member_id, role_id ); - drop(belongs_to); let request = Client::new().put(url).bearer_auth(user.token()); handle_request_as_option(request, user, crate::api::limits::LimitType::Guild).await } @@ -90,15 +86,13 @@ impl types::GuildMember { member_id: &str, role_id: &str, ) -> Option { - let belongs_to = user.belongs_to.borrow(); let url = format!( "{}/guilds/{}/members/{}/roles/{}/", - belongs_to.urls.get_api(), + user.belongs_to.borrow().urls.get_api(), guild_id, member_id, role_id ); - drop(belongs_to); let request = Client::new().delete(url).bearer_auth(user.token()); handle_request_as_option(request, user, crate::api::limits::LimitType::Guild).await } diff --git a/src/api/guilds/roles.rs b/src/api/guilds/roles.rs index 15a2915..dd76bf6 100644 --- a/src/api/guilds/roles.rs +++ b/src/api/guilds/roles.rs @@ -27,9 +27,11 @@ impl types::RoleObject { user: &mut UserMeta, guild_id: &str, ) -> Result>, ChorusLibError> { - let belongs_to = user.belongs_to.borrow(); - let url = format!("{}/guilds/{}/roles/", belongs_to.urls.get_api(), guild_id); - drop(belongs_to); + let url = format!( + "{}/guilds/{}/roles/", + user.belongs_to.borrow().urls.get_api(), + guild_id + ); let request = Client::new().get(url).bearer_auth(user.token()); let roles = deserialize_response::>( request, @@ -64,14 +66,12 @@ impl types::RoleObject { guild_id: &str, role_id: &str, ) -> Result { - let belongs_to = user.belongs_to.borrow(); let url = format!( "{}/guilds/{}/roles/{}/", - belongs_to.urls.get_api(), + user.belongs_to.borrow().urls.get_api(), guild_id, role_id ); - drop(belongs_to); let request = Client::new().get(url).bearer_auth(user.token()); deserialize_response(request, user, crate::api::limits::LimitType::Guild).await } @@ -96,17 +96,16 @@ impl types::RoleObject { guild_id: &str, role_create_schema: RoleCreateModifySchema, ) -> Result { - let belongs_to = user.belongs_to.borrow(); - let url = format!("{}/guilds/{}/roles/", belongs_to.urls.get_api(), guild_id); - drop(belongs_to); - let body = match to_string::(&role_create_schema) { - Ok(string) => string, - Err(e) => { - return Err(ChorusLibError::FormCreationError { - error: e.to_string(), - }); + let url = format!( + "{}/guilds/{}/roles/", + user.belongs_to.borrow().urls.get_api(), + guild_id + ); + let body = to_string::(&role_create_schema).map_err(|e| { + ChorusLibError::FormCreationError { + error: e.to_string(), } - }; + })?; let request = Client::new().post(url).bearer_auth(user.token()).body(body); deserialize_response(request, user, crate::api::limits::LimitType::Guild).await } @@ -131,17 +130,16 @@ impl types::RoleObject { guild_id: &str, role_position_update_schema: types::RolePositionUpdateSchema, ) -> Result { - let belongs_to = user.belongs_to.borrow(); - let url = format!("{}/guilds/{}/roles/", belongs_to.urls.get_api(), guild_id); - let body = match to_string(&role_position_update_schema) { - Ok(body) => body, - Err(e) => { - return Err(ChorusLibError::FormCreationError { - error: e.to_string(), - }); + let url = format!( + "{}/guilds/{}/roles/", + user.belongs_to.borrow().urls.get_api(), + guild_id + ); + let body = to_string(&role_position_update_schema).map_err(|e| { + ChorusLibError::FormCreationError { + error: e.to_string(), } - }; - drop(belongs_to); + })?; let request = Client::new() .patch(url) .bearer_auth(user.token()) @@ -172,22 +170,17 @@ impl types::RoleObject { role_id: &str, role_create_schema: RoleCreateModifySchema, ) -> Result { - let belongs_to = user.belongs_to.borrow(); let url = format!( "{}/guilds/{}/roles/{}", - belongs_to.urls.get_api(), + user.belongs_to.borrow().urls.get_api(), guild_id, role_id ); - drop(belongs_to); - let body = match to_string::(&role_create_schema) { - Ok(string) => string, - Err(e) => { - return Err(ChorusLibError::FormCreationError { - error: e.to_string(), - }); + let body = to_string::(&role_create_schema).map_err(|e| { + ChorusLibError::FormCreationError { + error: e.to_string(), } - }; + })?; let request = Client::new() .patch(url) .bearer_auth(user.token()) diff --git a/src/api/users/relationships.rs b/src/api/users/relationships.rs index 65c044e..0a09e2d 100644 --- a/src/api/users/relationships.rs +++ b/src/api/users/relationships.rs @@ -21,13 +21,11 @@ impl UserMeta { &mut self, user_id: &str, ) -> Result, ChorusLibError> { - let belongs_to = self.belongs_to.borrow(); let url = format!( "{}/users/{}/relationships/", - belongs_to.urls.get_api(), + self.belongs_to.borrow().urls.get_api(), user_id ); - drop(belongs_to); let request = Client::new().get(url).bearer_auth(self.token()); deserialize_response::>( request, @@ -42,9 +40,10 @@ impl UserMeta { /// # Returns /// This function returns a [`Result, ChorusLibError>`]. pub async fn get_relationships(&mut self) -> Result, ChorusLibError> { - let belongs_to = self.belongs_to.borrow(); - let url = format!("{}/users/@me/relationships/", belongs_to.urls.get_api(),); - drop(belongs_to); + let url = format!( + "{}/users/@me/relationships/", + self.belongs_to.borrow().urls.get_api() + ); let request = Client::new().get(url).bearer_auth(self.token()); deserialize_response::>( request, @@ -66,9 +65,10 @@ impl UserMeta { &mut self, schema: types::FriendRequestSendSchema, ) -> Option { - let belongs_to = self.belongs_to.borrow(); - let url = format!("{}/users/@me/relationships/", belongs_to.urls.get_api()); - drop(belongs_to); + let url = format!( + "{}/users/@me/relationships/", + self.belongs_to.borrow().urls.get_api() + ); let body = to_string(&schema).unwrap(); let request = Client::new().post(url).bearer_auth(self.token()).body(body); handle_request_as_option(request, self, crate::api::limits::LimitType::Global).await @@ -93,9 +93,7 @@ impl UserMeta { user_id: &str, relationship_type: RelationshipType, ) -> Option { - let belongs_to = self.belongs_to.borrow(); - let api_url = belongs_to.urls.api.clone(); - drop(belongs_to); + let api_url = self.belongs_to.borrow().urls.api.clone(); match relationship_type { RelationshipType::None => { let request = Client::new() @@ -140,13 +138,11 @@ impl UserMeta { /// # Returns /// This function returns an [`Option`] that holds a [`ChorusLibError`] if the request fails. pub async fn remove_relationship(&mut self, user_id: &str) -> Option { - let belongs_to = self.belongs_to.borrow(); let url = format!( "{}/users/@me/relationships/{}/", - belongs_to.urls.get_api(), + self.belongs_to.borrow().urls.get_api(), user_id ); - drop(belongs_to); let request = Client::new().post(url).bearer_auth(self.token()); handle_request_as_option(request, self, crate::api::limits::LimitType::Global).await } diff --git a/src/api/users/users.rs b/src/api/users/users.rs index 8423e01..2e2485e 100644 --- a/src/api/users/users.rs +++ b/src/api/users/users.rs @@ -76,11 +76,12 @@ impl UserMeta { /// /// Returns `None` if the user was successfully deleted, or an `ChorusLibError` if an error occurred. pub async fn delete(mut self) -> Option { - let belongs_to = self.belongs_to.borrow(); let request = Client::new() - .post(format!("{}/users/@me/delete/", belongs_to.urls.get_api())) + .post(format!( + "{}/users/@me/delete/", + self.belongs_to.borrow().urls.get_api() + )) .bearer_auth(self.token()); - drop(belongs_to); handle_request_as_option(request, &mut self, crate::api::limits::LimitType::Ip).await } } From 0a1de13f2948b4e0f701dd771fa4baa3bb8cd497 Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Mon, 19 Jun 2023 18:11:53 +0200 Subject: [PATCH 23/34] Add a friendship test (:3) --- tests/relationships.rs | 59 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/tests/relationships.rs b/tests/relationships.rs index 187f07e..bdb5e68 100644 --- a/tests/relationships.rs +++ b/tests/relationships.rs @@ -1,4 +1,4 @@ -use chorus::types::{self, RegisterSchema, RegisterSchemaOptions}; +use chorus::types::{self, RegisterSchema, RegisterSchemaOptions, Relationship, RelationshipType}; mod common; @@ -47,6 +47,61 @@ async fn test_get_relationships() { }; other_user.send_friend_request(friend_request_schema).await; let relationships = user.get_relationships().await.unwrap(); - println!("{:?}", relationships); + assert_eq!(relationships.get(0).unwrap().id, other_user.object.id); + common::teardown(bundle).await +} + +#[tokio::test] +async fn test_modify_relationship_friends() { + let register_schema = RegisterSchemaOptions { + date_of_birth: Some("2000-01-01".to_string()), + ..RegisterSchema::builder("integrationtestuser2", true) + } + .build() + .unwrap(); + + let mut bundle = common::setup().await; + let belongs_to = &mut bundle.instance; + let user = &mut bundle.user; + let mut other_user = belongs_to.register_account(®ister_schema).await.unwrap(); + other_user + .modify_user_relationship( + &user.object.id.to_string(), + types::RelationshipType::Friends, + ) + .await; + let relationships = user.get_relationships().await.unwrap(); + assert_eq!(relationships.get(0).unwrap().id, other_user.object.id); + assert_eq!( + relationships.get(0).unwrap().relationship_type, + RelationshipType::Incoming + ); + let relationships = other_user.get_relationships().await.unwrap(); + assert_eq!(relationships.get(0).unwrap().id, user.object.id); + assert_eq!( + relationships.get(0).unwrap().relationship_type, + RelationshipType::Outgoing + ); + user.modify_user_relationship( + other_user.object.id.to_string().as_str(), + RelationshipType::Friends, + ) + .await; + assert_eq!( + other_user + .get_relationships() + .await + .unwrap() + .get(0) + .unwrap() + .relationship_type, + RelationshipType::Friends + ); + user.remove_relationship(other_user.object.id.to_string().as_str()) + .await; + assert_eq!( + other_user.get_relationships().await.unwrap(), + Vec::::new() + ); common::teardown(bundle).await } From 06b4f5fb657a08838e3648a3940f12dc9fc5f5a9 Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Mon, 19 Jun 2023 18:12:01 +0200 Subject: [PATCH 24/34] Add Eq, PartialEq derives --- src/types/entities/relationship.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/entities/relationship.rs b/src/types/entities/relationship.rs index b965907..a6abc09 100644 --- a/src/types/entities/relationship.rs +++ b/src/types/entities/relationship.rs @@ -6,7 +6,7 @@ use crate::types::Snowflake; use super::PublicUser; -#[derive(Debug, Deserialize, Serialize, Clone, Default)] +#[derive(Debug, Deserialize, Serialize, Clone, Default, PartialEq, Eq)] /// See https://discord-userdoccers.vercel.app/resources/user#relationship-structure pub struct Relationship { pub id: Snowflake, @@ -17,7 +17,7 @@ pub struct Relationship { pub since: Option>, } -#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)] +#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default, Eq, PartialEq)] #[repr(u8)] /// See https://discord-userdoccers.vercel.app/resources/user#relationship-type pub enum RelationshipType { From 17223d338aa403a169112fb791897c5248060df6 Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Mon, 19 Jun 2023 18:12:09 +0200 Subject: [PATCH 25/34] change post to delete --- src/api/users/relationships.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/users/relationships.rs b/src/api/users/relationships.rs index 0a09e2d..fae5725 100644 --- a/src/api/users/relationships.rs +++ b/src/api/users/relationships.rs @@ -143,7 +143,7 @@ impl UserMeta { self.belongs_to.borrow().urls.get_api(), user_id ); - let request = Client::new().post(url).bearer_auth(self.token()); + let request = Client::new().delete(url).bearer_auth(self.token()); handle_request_as_option(request, self, crate::api::limits::LimitType::Global).await } } From f7ea86917fae12de14ebae935e35b2f2989b0579 Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Mon, 19 Jun 2023 18:46:49 +0200 Subject: [PATCH 26/34] Add block and unblock test --- tests/relationships.rs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/relationships.rs b/tests/relationships.rs index bdb5e68..474b015 100644 --- a/tests/relationships.rs +++ b/tests/relationships.rs @@ -105,3 +105,40 @@ async fn test_modify_relationship_friends() { ); common::teardown(bundle).await } + +#[tokio::test] +async fn test_modify_relationship_block() { + let register_schema = RegisterSchemaOptions { + date_of_birth: Some("2000-01-01".to_string()), + ..RegisterSchema::builder("integrationtestuser2", true) + } + .build() + .unwrap(); + + let mut bundle = common::setup().await; + let belongs_to = &mut bundle.instance; + let user = &mut bundle.user; + let mut other_user = belongs_to.register_account(®ister_schema).await.unwrap(); + other_user + .modify_user_relationship( + &user.object.id.to_string(), + types::RelationshipType::Blocked, + ) + .await; + let relationships = user.get_relationships().await.unwrap(); + assert_eq!(relationships, Vec::::new()); + let relationships = other_user.get_relationships().await.unwrap(); + assert_eq!(relationships.get(0).unwrap().id, user.object.id); + assert_eq!( + relationships.get(0).unwrap().relationship_type, + RelationshipType::Blocked + ); + other_user + .remove_relationship(user.object.id.to_string().as_str()) + .await; + assert_eq!( + other_user.get_relationships().await.unwrap(), + Vec::::new() + ); + common::teardown(bundle).await +} From 603c8c8150863ab7d56f1f9c83ec5814c8e5d782 Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Mon, 19 Jun 2023 19:01:18 +0200 Subject: [PATCH 27/34] apply clippy suggestions --- src/api/channels/channels.rs | 2 +- src/api/common.rs | 4 ++-- src/api/guilds/guilds.rs | 2 +- src/gateway.rs | 6 ++---- tests/common/mod.rs | 1 + 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/api/channels/channels.rs b/src/api/channels/channels.rs index 7c4c8aa..8c96472 100644 --- a/src/api/channels/channels.rs +++ b/src/api/channels/channels.rs @@ -48,7 +48,7 @@ impl Channel { .delete(format!( "{}/channels/{}/", user.belongs_to.borrow_mut().urls.get_api(), - self.id.to_string() + self.id )) .bearer_auth(user.token()); let response = diff --git a/src/api/common.rs b/src/api/common.rs index cecec5a..af9d975 100644 --- a/src/api/common.rs +++ b/src/api/common.rs @@ -48,7 +48,7 @@ pub async fn deserialize_response Deserialize<'a>>( return Err(ChorusLibError::InvalidResponseError { error: format!( "Error while trying to process the HTTP response into a String: {}", - e.to_string() + e ), }); } @@ -59,7 +59,7 @@ pub async fn deserialize_response Deserialize<'a>>( return Err(ChorusLibError::InvalidResponseError { error: format!( "Error while trying to deserialize the JSON response into T: {}", - e.to_string() + e ), }) } diff --git a/src/api/guilds/guilds.rs b/src/api/guilds/guilds.rs index 6fea804..95c3bc2 100644 --- a/src/api/guilds/guilds.rs +++ b/src/api/guilds/guilds.rs @@ -120,7 +120,7 @@ impl Guild { .get(format!( "{}/guilds/{}/channels/", user.belongs_to.borrow().urls.get_api(), - self.id.to_string() + self.id )) .bearer_auth(user.token()); let result = handle_request(request, user, crate::api::limits::LimitType::Channel) diff --git a/src/gateway.rs b/src/gateway.rs index 28ed702..5cececa 100644 --- a/src/gateway.rs +++ b/src/gateway.rs @@ -417,11 +417,9 @@ impl Gateway { if msg.is_error() { println!("GW: Received error, connection will close.."); - let error = msg.error(); + let _error = msg.error(); - match error { - _ => {} - } + {} self.close().await; return; diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 5a29a0b..224014a 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -93,6 +93,7 @@ pub async fn setup() -> TestBundle { } // Teardown method to clean up after a test. +#[allow(dead_code)] pub async fn teardown(mut bundle: TestBundle) { Guild::delete(&mut bundle.user, &bundle.guild.id.to_string()).await; bundle.user.delete().await; From e03cafd23f5a71dbb107600b843871118543e8f5 Mon Sep 17 00:00:00 2001 From: Flori <39242991+bitfl0wer@users.noreply.github.com> Date: Mon, 19 Jun 2023 23:42:56 +0200 Subject: [PATCH 28/34] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 206d7f9..279a196 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![Contributors][contributors-shield]][contributors-url] [![Forks][forks-shield]][forks-url] [![Issues][issues-shield]][issues-url] +
From 9942a4b1000339644247f7b2c946a5d55e22f498 Mon Sep 17 00:00:00 2001 From: Flori Weber Date: Tue, 20 Jun 2023 00:17:52 +0200 Subject: [PATCH 29/34] Simplify README --- README.md | 171 +++++++++++++++++++++++++++++------------------------- 1 file changed, 92 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index 279a196..9a75a54 100644 --- a/README.md +++ b/README.md @@ -31,93 +31,106 @@
-## Progress Tracker/Roadmap: -### Core Functionality -- [x] Rate Limiter (hint: couldn't be fully tested due to [an Issue with the Spacebar Server](https://github.com/spacebarchat/server/issues/1022)) -- [x] [Login (the conventional way)](https://github.com/polyphony-chat/chorus/issues/1) -- [ ] [2FA](https://github.com/polyphony-chat/chorus/issues/40) -- [x] [Registration](https://github.com/polyphony-chat/chorus/issues/1) +## About -### Messaging -- [x] [Sending messages](https://github.com/polyphony-chat/chorus/issues/23) -- [x] [Events (Message, User, Channel, etc.)](https://github.com/polyphony-chat/chorus/issues/51) -- [x] Channel creation -- [x] Channel deletion -- [x] [Channel management (name, description, icon, etc.)](https://github.com/polyphony-chat/chorus/issues/48) -- [ ] [Join and Leave Guilds](https://github.com/polyphony-chat/chorus/issues/45) -- [ ] [Start DMs](https://github.com/polyphony-chat/chorus/issues/45) -- [ ] [Group DM creation, deletion and member management](https://github.com/polyphony-chat/chorus/issues/89) -- [ ] [Deleting messages](https://github.com/polyphony-chat/chorus/issues/91) -- [ ] [Message threads](https://github.com/polyphony-chat/chorus/issues/90) -- [x] [Reactions](https://github.com/polyphony-chat/chorus/issues/85) -- [ ] Message Search -- [ ] Message history -- [ ] Emoji -- [ ] Stickers -- [ ] [Forum channels](https://github.com/polyphony-chat/chorus/issues/90) +Chorus is a Rust library that allows developers to interact with multiple Spacebar-compatible APIs and Gateways simultaneously. The library provides a simple and efficient way to communicate with these services, making it easier for developers to build applications that rely on them. Chorus is open-source and welcomes contributions from the community. -### User Management -- [ ] [User profile customization](https://github.com/polyphony-chat/chorus/issues/41) -- [x] Gettings users and user profiles -- [x] [Friend requests](https://github.com/polyphony-chat/chorus/issues/92) -- [x] [Blocking users](https://github.com/polyphony-chat/chorus/issues/92) -- [ ] User presence (online, offline, idle, etc.) -- [ ] User status (custom status, etc.) -- [x] Account deletion +## Contributing -### Additional Features -- [ ] Server discovery -- [ ] Server templates +If you would like to contribute, please feel free to open an Issue with the idea you have, or a +Pull Request. Please keep our [contribution guidelines](https://github.com/polyphony-chat/.github/blob/main/CONTRIBUTION_GUIDELINES.md) in mind. Your contribution might not be +accepted, if it violates these guidelines or [our Code of Conduct](https://github.com/polyphony-chat/.github/blob/main/CODE_OF_CONDUCT.md). -### Voice and Video -- [ ] [Voice chat support](https://github.com/polyphony-chat/chorus/issues/49) -- [ ] [Video chat support](https://github.com/polyphony-chat/chorus/issues/49) +
+ Progress Tracker/Roadmap -### Permissions and Roles -- [x] [Role management](https://github.com/polyphony-chat/chorus/issues/46) (creation, deletion, modification) -- [x] [Permission management](https://github.com/polyphony-chat/chorus/issues/46) (assigning and revoking permissions) -- [x] [Channel-specific permissions](https://github.com/polyphony-chat/chorus/issues/88) -- [x] Role-based access control + ### Core Functionality + - [x] Rate Limiter (hint: couldn't be fully tested due to [an Issue with the Spacebar Server](https://github.com/spacebarchat/server/issues/1022)) + - [x] [Login (the conventional way)](https://github.com/polyphony-chat/chorus/issues/1) + - [ ] [2FA](https://github.com/polyphony-chat/chorus/issues/40) + - [x] [Registration](https://github.com/polyphony-chat/chorus/issues/1) -### Guild Management -- [x] Guild creation -- [x] Guild deletion -- [ ] [Guild settings (name, description, icon, etc.)](https://github.com/polyphony-chat/chorus/issues/43) -- [ ] Guild invites + ### Messaging + - [x] [Sending messages](https://github.com/polyphony-chat/chorus/issues/23) + - [x] [Events (Message, User, Channel, etc.)](https://github.com/polyphony-chat/chorus/issues/51) + - [x] Channel creation + - [x] Channel deletion + - [x] [Channel management (name, description, icon, etc.)](https://github.com/polyphony-chat/chorus/issues/48) + - [ ] [Join and Leave Guilds](https://github.com/polyphony-chat/chorus/issues/45) + - [ ] [Start DMs](https://github.com/polyphony-chat/chorus/issues/45) + - [ ] [Group DM creation, deletion and member management](https://github.com/polyphony-chat/chorus/issues/89) + - [ ] [Deleting messages](https://github.com/polyphony-chat/chorus/issues/91) + - [ ] [Message threads](https://github.com/polyphony-chat/chorus/issues/90) + - [x] [Reactions](https://github.com/polyphony-chat/chorus/issues/85) + - [ ] Message Search + - [ ] Message history + - [ ] Emoji + - [ ] Stickers + - [ ] [Forum channels](https://github.com/polyphony-chat/chorus/issues/90) -### Moderation -- [ ] Channel moderation (slow mode, etc.) -- [ ] User sanctions (mute, kick, ban) -- [ ] Audit logs + ### User Management + - [ ] [User profile customization](https://github.com/polyphony-chat/chorus/issues/41) + - [x] Gettings users and user profiles + - [x] [Friend requests](https://github.com/polyphony-chat/chorus/issues/92) + - [x] [Blocking users](https://github.com/polyphony-chat/chorus/issues/92) + - [ ] User presence (online, offline, idle, etc.) + - [ ] User status (custom status, etc.) + - [x] Account deletion -### Embeds and Rich Content -- [x] Sending rich content in messages (links, images, videos) -- [ ] Customizing embed appearance (title, description, color, fields) + ### Additional Features + - [ ] Server discovery + - [ ] Server templates -### Webhooks -- [ ] Webhook creation and management -- [ ] Handling incoming webhook events + ### Voice and Video + - [ ] [Voice chat support](https://github.com/polyphony-chat/chorus/issues/49) + - [ ] [Video chat support](https://github.com/polyphony-chat/chorus/issues/49) -### Documentation and Examples -- [ ] Comprehensive documentation -- [ ] Example usage and code snippets -- [ ] Tutorials and guides + ### Permissions and Roles + - [x] [Role management](https://github.com/polyphony-chat/chorus/issues/46) (creation, deletion, modification) + - [x] [Permission management](https://github.com/polyphony-chat/chorus/issues/46) (assigning and revoking permissions) + - [x] [Channel-specific permissions](https://github.com/polyphony-chat/chorus/issues/88) + - [x] Role-based access control -[Rust]: https://img.shields.io/badge/Rust-orange?style=plastic&logo=rust -[Rust-url]: https://www.rust-lang.org/ -[build-shield]: https://img.shields.io/github/actions/workflow/status/polyphony-chat/chorus/build_and_test.yml?style=flat -[build-url]: https://github.com/polyphony-chat/chorus/blob/main/.github/workflows/build_and_test.yml -[clippy-shield]: https://img.shields.io/github/actions/workflow/status/polyphony-chat/chorus/clippy.yml?style=flat -[clippy-url]: https://github.com/polyphony-chat/chorus/blob/main/.github/workflows/clippy.yml -[contributors-shield]: https://img.shields.io/github/contributors/polyphony-chat/chorus.svg?style=flat -[contributors-url]: https://github.com/polyphony-chat/chorus/graphs/contributors -[forks-shield]: https://img.shields.io/github/forks/polyphony-chat/chorus.svg?style=flat -[forks-url]: https://github.com/polyphony-chat/chorus/network/members -[stars-shield]: https://img.shields.io/github/stars/polyphony-chat/chorus.svg?style=flat -[stars-url]: https://github.com/polyphony-chat/chorus/stargazers -[issues-shield]: https://img.shields.io/github/issues/polyphony-chat/chorus.svg?style=flat -[issues-url]: https://github.com/polyphony-chat/chorus/issues -[license-shield]: https://img.shields.io/github/license/polyphony-chat/chorus.svg?style=f;at -[license-url]: https://github.com/polyphony-chat/chorus/blob/master/LICENSE -[Discord]: https://dcbadge.vercel.app/api/server/m3FpcapGDD?style=flat -[Discord-invite]: https://discord.com/invite/m3FpcapGDD + ### Guild Management + - [x] Guild creation + - [x] Guild deletion + - [ ] [Guild settings (name, description, icon, etc.)](https://github.com/polyphony-chat/chorus/issues/43) + - [ ] Guild invites + + ### Moderation + - [ ] Channel moderation (slow mode, etc.) + - [ ] User sanctions (mute, kick, ban) + - [ ] Audit logs + + ### Embeds and Rich Content + - [x] Sending rich content in messages (links, images, videos) + - [ ] Customizing embed appearance (title, description, color, fields) + + ### Webhooks + - [ ] Webhook creation and management + - [ ] Handling incoming webhook events + + ### Documentation and Examples + - [ ] Comprehensive documentation + - [ ] Example usage and code snippets + - [ ] Tutorials and guides + + [Rust]: https://img.shields.io/badge/Rust-orange?style=plastic&logo=rust + [Rust-url]: https://www.rust-lang.org/ + [build-shield]: https://img.shields.io/github/actions/workflow/status/polyphony-chat/chorus/build_and_test.yml?style=flat + [build-url]: https://github.com/polyphony-chat/chorus/blob/main/.github/workflows/build_and_test.yml + [clippy-shield]: https://img.shields.io/github/actions/workflow/status/polyphony-chat/chorus/clippy.yml?style=flat + [clippy-url]: https://github.com/polyphony-chat/chorus/blob/main/.github/workflows/clippy.yml + [contributors-shield]: https://img.shields.io/github/contributors/polyphony-chat/chorus.svg?style=flat + [contributors-url]: https://github.com/polyphony-chat/chorus/graphs/contributors + [forks-shield]: https://img.shields.io/github/forks/polyphony-chat/chorus.svg?style=flat + [forks-url]: https://github.com/polyphony-chat/chorus/network/members + [stars-shield]: https://img.shields.io/github/stars/polyphony-chat/chorus.svg?style=flat + [stars-url]: https://github.com/polyphony-chat/chorus/stargazers + [issues-shield]: https://img.shields.io/github/issues/polyphony-chat/chorus.svg?style=flat + [issues-url]: https://github.com/polyphony-chat/chorus/issues + [license-shield]: https://img.shields.io/github/license/polyphony-chat/chorus.svg?style=f;at + [license-url]: https://github.com/polyphony-chat/chorus/blob/master/LICENSE + [Discord]: https://dcbadge.vercel.app/api/server/m3FpcapGDD?style=flat + [Discord-invite]: https://discord.com/invite/m3FpcapGDD +
\ No newline at end of file From 29fcdfe641d16de68ec61ff1423b3ec28fbe8108 Mon Sep 17 00:00:00 2001 From: Vincent Junge Date: Tue, 20 Jun 2023 02:30:46 +0200 Subject: [PATCH 30/34] simplyfy Snowflake --- Cargo.toml | 3 - src/types/config/mod.rs | 7 +- src/types/utils/mod.rs | 2 +- src/types/utils/snowflake.rs | 197 +++++++++++++---------------------- 4 files changed, 77 insertions(+), 132 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8e33c0d..cece3e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,9 +29,6 @@ openssl = "0.10.52" base64 = "0.21.2" hostname = "0.3.1" bitflags = { version = "2.2.1", features = ["serde"] } -atomic = "0.5.3" -bigdecimal = "0.3.1" -num-bigint = "0.4.3" lazy_static = "1.4.0" poem = { version = "1.3.55", optional = true } sqlx = { git = "https://github.com/zert3x/sqlx", branch="feature/skip", features = ["mysql", "sqlite", "json", "chrono", "ipnetwork", "runtime-tokio-native-tls", "any"], optional = true } diff --git a/src/types/config/mod.rs b/src/types/config/mod.rs index 521d303..4a11c71 100644 --- a/src/types/config/mod.rs +++ b/src/types/config/mod.rs @@ -89,15 +89,15 @@ fn generate_pairs(obj: &Value, key: &str) -> Vec { fn pairs_to_config(pairs: Vec) -> ConfigValue { let mut value = Value::Object(Map::new()); - for p in pairs { - let keys: Vec<&str> = p.key.split('_').collect(); + for pair in pairs { + let keys: Vec<&str> = pair.key.split('_').collect(); let mut path = vec![]; for (i, &key) in keys.iter().enumerate() { path.push(key); if i == keys.len() - 1 { - insert_into(&mut value, &path, p.value.clone().unwrap_or(Value::Null)); + insert_into(&mut value, &path, pair.value.clone().unwrap_or(Value::Null)); } else if keys[i + 1].parse::().is_ok() { if !path_exists(&value, &path) { insert_into(&mut value, &path, Value::Array(Vec::new())); @@ -182,6 +182,7 @@ mod test { let pairs = generate_pairs(&v, ""); let cfg = pairs_to_config(pairs); + assert_eq!(cfg, c) } } diff --git a/src/types/utils/mod.rs b/src/types/utils/mod.rs index 7fd199f..1b1b3b6 100644 --- a/src/types/utils/mod.rs +++ b/src/types/utils/mod.rs @@ -1,6 +1,6 @@ pub use regexes::*; pub use rights::Rights; -pub use snowflake::{DeconstructedSnowflake, Snowflake}; +pub use snowflake::Snowflake; pub mod jwt; mod regexes; diff --git a/src/types/utils/snowflake.rs b/src/types/utils/snowflake.rs index 7c756fa..8502275 100644 --- a/src/types/utils/snowflake.rs +++ b/src/types/utils/snowflake.rs @@ -1,22 +1,41 @@ -use std::fmt::Display; +use std::{ + fmt::Display, + sync::atomic::{AtomicUsize, Ordering}, +}; -use atomic::Atomic; -use bigdecimal::{Num, ToPrimitive, Zero}; -use num_bigint::{BigInt, ToBigInt}; -use serde::{Deserialize, Serialize}; +use chrono::{DateTime, TimeZone, Utc}; #[cfg(feature = "sqlx")] use sqlx::Type; +/// 2015-01-01 const EPOCH: i64 = 1420070400000; -static WORKER_ID: u128 = 0; -static PROCESS_ID: u128 = 1; -lazy_static::lazy_static! { - static ref INCREMENT: Atomic = Atomic::default(); -} -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] + +/// Unique identifier including a timestamp. +/// See https://discord.com/developers/docs/reference#snowflakes +#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "sqlx", derive(Type))] #[cfg_attr(feature = "sqlx", sqlx(transparent))] -pub struct Snowflake(String); +pub struct Snowflake(u64); + +impl Snowflake { + pub fn generate() -> Self { + const WORKER_ID: u64 = 0; + const PROCESS_ID: u64 = 1; + static INCREMENT: AtomicUsize = AtomicUsize::new(0); + + let time = (Utc::now().naive_utc().timestamp_millis() - EPOCH) << 22; + let worker = WORKER_ID << 17; + let process = PROCESS_ID << 12; + let increment = INCREMENT.fetch_add(1, Ordering::Relaxed) as u64 % 32; + + Self(time as u64 | worker | process | increment) + } + + pub fn timestamp(self) -> DateTime { + Utc.timestamp_millis_opt((self.0 >> 22) as i64 + EPOCH) + .unwrap() + } +} impl Default for Snowflake { fn default() -> Self { @@ -30,131 +49,59 @@ impl Display for Snowflake { } } -impl Snowflake { - pub fn to_binary(&self) -> String { - let self_len = self.0.len(); - let high = self.0[..self_len - 10].parse::().unwrap_or(0); - let low = self.0[self_len - 10..].parse::().unwrap(); - let mut low = low; - let mut high = high; - let mut bin = Vec::with_capacity(64); - - while low > 0 || high > 0 { - bin.push((low & 1) as u8); - low >>= 1; - - if high > 0 { - low += 5_000_000_000 * (high % 2); - high >>= 1; - } - } - - bin.iter() - .rev() - .map(|b| char::from_digit(*b as u32, 10).unwrap()) - .collect() - } - - pub fn from_binary(num: &str) -> String { - let mut num = BigInt::from_str_radix(num, 2).unwrap(); - let mut dec = Vec::with_capacity(18); - - let ten = 10.to_bigint().unwrap(); - let _two = 2.to_bigint().unwrap(); - let _thirty_two = 32.to_bigint().unwrap(); - - while num.bits() > 50 { - let high: BigInt = &num >> 32; - let low: BigInt = (high.clone() % &ten) << 32 | &num & BigInt::from((1u64 << 32) - 1); - - let next: BigInt = low.clone() % &ten; - dec.push(next.to_u8().unwrap()); - num = (high / &ten) << 32 | (low / &ten); - } - - while !num.is_zero() { - dec.push((num.clone() % &ten).to_u8().unwrap()); - num /= &ten; - } - - dec.iter() - .rev() - .map(|d| char::from_digit(*d as u32, 10).unwrap()) - .collect() - } - - pub fn generate_worker_process() -> u128 { - let time = (chrono::Utc::now().naive_utc().timestamp_millis() - EPOCH) << 22; - let worker = WORKER_ID << 17; - let process = PROCESS_ID << 12; - let increment = INCREMENT.load(atomic::Ordering::Relaxed); - - INCREMENT.store(increment + 1, atomic::Ordering::Relaxed); - - time as u128 | worker | process | increment - } - - pub fn generate() -> Self { - Self(Self::generate_worker_process().to_string()) - } - - pub fn deconstruct(&self) -> DeconstructedSnowflake { - let binary = format!("{:0>64}", self.to_binary()); - - let ts = i64::from_str_radix(&binary[0..42], 2).unwrap() + EPOCH; - let wid = u64::from_str_radix(&binary[42..47], 2).unwrap(); - let pid = u64::from_str_radix(&binary[47..52], 2).unwrap(); - let increment = BigInt::from_str_radix(&binary[52..64], 2).unwrap(); - - DeconstructedSnowflake { - timestamp: ts, - worker_id: wid, - process_id: pid, - increment, - binary, - } +impl serde::Serialize for Snowflake { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.0.to_string()) } } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct DeconstructedSnowflake { - pub timestamp: i64, - pub worker_id: u64, - pub process_id: u64, - pub increment: BigInt, - pub binary: String, +impl<'de> serde::Deserialize<'de> for Snowflake { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct SnowflakeVisitor; + impl<'de> serde::de::Visitor<'de> for SnowflakeVisitor { + type Value = Snowflake; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("snowflake string") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + match value.parse() { + Ok(value) => Ok(Snowflake(value)), + Err(_) => Err(serde::de::Error::custom("")), + } + } + } + deserializer.deserialize_str(SnowflakeVisitor) + } } #[cfg(test)] mod test { + use chrono::{DateTime, Utc}; + use super::Snowflake; #[test] - fn test_new_snowflake() { - let snow = Snowflake::generate(); - println!("{snow}"); + fn generate() { + let snow_1 = Snowflake::generate(); + let snow_2 = Snowflake::generate(); + assert!(snow_1.0 < snow_2.0) } #[test] - fn snowflake_to_binary() { - let snowflake = super::Snowflake("1104339392517902336".to_string()); - - let bin = snowflake.to_binary(); - println!("{bin}"); - } - - #[test] - fn binary_to_snowflake() { - let snowflake = super::Snowflake::from_binary( - "111101010011011001101101001110010010100000000001000000000000", - ); - println!("{snowflake}"); - } - - #[test] - fn test_deconstruct() { - let new = super::Snowflake::generate(); - - println!("{:?}", new.deconstruct()); + fn timestamp() { + let snow: Snowflake = serde_json::from_str("\"175928847299117063\"").unwrap(); + let timestamp = "2016-04-30 11:18:25.796Z".parse::>().unwrap(); + assert_eq!(snow.timestamp(), timestamp); } } From f614e938924e077165279493489a37b48ea9b3df Mon Sep 17 00:00:00 2001 From: Vincent Junge Date: Tue, 20 Jun 2023 02:59:18 +0200 Subject: [PATCH 31/34] UrlBundle remove getters --- src/api/auth/login.rs | 2 +- src/api/auth/register.rs | 9 ++++--- src/api/channels/channels.rs | 6 ++--- src/api/channels/messages.rs | 2 +- src/api/channels/permissions.rs | 4 ++-- src/api/channels/reactions.rs | 12 +++++----- src/api/guilds/guilds.rs | 12 +++++----- src/api/guilds/member.rs | 6 ++--- src/api/guilds/roles.rs | 10 ++++---- src/api/policies/instance/instance.rs | 5 ++-- src/api/policies/instance/limits.rs | 2 +- src/api/users/relationships.rs | 8 +++---- src/api/users/users.rs | 11 ++++----- src/instance.rs | 6 ++--- src/lib.rs | 34 +++++++++------------------ src/limit.rs | 6 ++--- tests/common/mod.rs | 6 ++--- 17 files changed, 62 insertions(+), 79 deletions(-) diff --git a/src/api/auth/login.rs b/src/api/auth/login.rs index 73abf24..28b50fe 100644 --- a/src/api/auth/login.rs +++ b/src/api/auth/login.rs @@ -17,7 +17,7 @@ impl Instance { ) -> Result { let json_schema = json!(login_schema); let client = Client::new(); - let endpoint_url = self.urls.get_api().to_string() + "/auth/login"; + let endpoint_url = self.urls.api.clone() + "/auth/login"; let request_builder = client.post(endpoint_url).body(json_schema.to_string()); // We do not have a user yet, and the UserRateLimits will not be affected by a login // request (since login is an instance wide limit), which is why we are just cloning the diff --git a/src/api/auth/register.rs b/src/api/auth/register.rs index 0698704..85196cb 100644 --- a/src/api/auth/register.rs +++ b/src/api/auth/register.rs @@ -25,7 +25,7 @@ impl Instance { ) -> Result { let json_schema = json!(register_schema); let client = Client::new(); - let endpoint_url = self.urls.get_api().to_string() + "/auth/register"; + let endpoint_url = self.urls.api.clone() + "/auth/register"; let request_builder = client.post(endpoint_url).body(json_schema.to_string()); // We do not have a user yet, and the UserRateLimits will not be affected by a login // request (since register is an instance wide limit), which is why we are just cloning @@ -59,10 +59,9 @@ impl Instance { return Err(ChorusLibError::InvalidFormBodyError { error_type, error }); } let user_object = self.get_user(token.clone(), None).await.unwrap(); - let settings = - UserMeta::get_settings(&token, &self.urls.get_api().to_string(), &mut self.limits) - .await - .unwrap(); + let settings = UserMeta::get_settings(&token, &self.urls.api, &mut self.limits) + .await + .unwrap(); let user = UserMeta::new( Rc::new(RefCell::new(self.clone())), token.clone(), diff --git a/src/api/channels/channels.rs b/src/api/channels/channels.rs index 8c96472..e0ede6a 100644 --- a/src/api/channels/channels.rs +++ b/src/api/channels/channels.rs @@ -10,7 +10,7 @@ use crate::{ impl Channel { pub async fn get(user: &mut UserMeta, channel_id: &str) -> Result { - let url = user.belongs_to.borrow_mut().urls.get_api().to_string(); + let url = user.belongs_to.borrow_mut().urls.api.clone(); let request = Client::new() .get(format!("{}/channels/{}/", url, channel_id)) .bearer_auth(user.token()); @@ -47,7 +47,7 @@ impl Channel { let request = Client::new() .delete(format!( "{}/channels/{}/", - user.belongs_to.borrow_mut().urls.get_api(), + user.belongs_to.borrow_mut().urls.api, self.id )) .bearer_auth(user.token()); @@ -78,7 +78,7 @@ impl Channel { let request = Client::new() .patch(format!( "{}/channels/{}/", - user.belongs_to.borrow().urls.get_api(), + user.belongs_to.borrow().urls.api, channel_id )) .bearer_auth(user.token()) diff --git a/src/api/channels/messages.rs b/src/api/channels/messages.rs index 281f8ff..61801a8 100644 --- a/src/api/channels/messages.rs +++ b/src/api/channels/messages.rs @@ -25,7 +25,7 @@ impl Message { message: &mut MessageSendSchema, files: Option>, ) -> Result { - let url_api = user.belongs_to.borrow().urls.get_api().to_string(); + let url_api = user.belongs_to.borrow().urls.api.clone(); if files.is_none() { let request = Client::new() diff --git a/src/api/channels/permissions.rs b/src/api/channels/permissions.rs index cd12f30..7871479 100644 --- a/src/api/channels/permissions.rs +++ b/src/api/channels/permissions.rs @@ -28,7 +28,7 @@ impl types::Channel { let url = { format!( "{}/channels/{}/permissions/{}", - user.belongs_to.borrow_mut().urls.get_api(), + user.belongs_to.borrow_mut().urls.api, channel_id, overwrite.id ) @@ -68,7 +68,7 @@ impl types::Channel { ) -> Option { let url = format!( "{}/channels/{}/permissions/{}", - user.belongs_to.borrow_mut().urls.get_api(), + user.belongs_to.borrow_mut().urls.api, channel_id, overwrite_id ); diff --git a/src/api/channels/reactions.rs b/src/api/channels/reactions.rs index 0f54002..dfd1838 100644 --- a/src/api/channels/reactions.rs +++ b/src/api/channels/reactions.rs @@ -33,7 +33,7 @@ impl ReactionMeta { pub async fn delete_all(&self, user: &mut UserMeta) -> Option { let url = format!( "{}/channels/{}/messages/{}/reactions/", - user.belongs_to.borrow().urls.get_api(), + user.belongs_to.borrow().urls.api, self.channel_id, self.message_id ); @@ -64,7 +64,7 @@ impl ReactionMeta { pub async fn get(&self, emoji: &str, user: &mut UserMeta) -> Option { let url = format!( "{}/channels/{}/messages/{}/reactions/{}/", - user.belongs_to.borrow().urls.get_api(), + user.belongs_to.borrow().urls.api, self.channel_id, self.message_id, emoji @@ -98,7 +98,7 @@ impl ReactionMeta { pub async fn delete_emoji(&self, emoji: &str, user: &mut UserMeta) -> Option { let url = format!( "{}/channels/{}/messages/{}/reactions/{}/", - user.belongs_to.borrow().urls.get_api(), + user.belongs_to.borrow().urls.api, self.channel_id, self.message_id, emoji @@ -136,7 +136,7 @@ impl ReactionMeta { pub async fn create(&self, emoji: &str, user: &mut UserMeta) -> Option { let url = format!( "{}/channels/{}/messages/{}/reactions/{}/@me/", - user.belongs_to.borrow().urls.get_api(), + user.belongs_to.borrow().urls.api, self.channel_id, self.message_id, emoji @@ -165,7 +165,7 @@ impl ReactionMeta { pub async fn remove(&self, emoji: &str, user: &mut UserMeta) -> Option { let url = format!( "{}/channels/{}/messages/{}/reactions/{}/@me/", - user.belongs_to.borrow().urls.get_api(), + user.belongs_to.borrow().urls.api, self.channel_id, self.message_id, emoji @@ -202,7 +202,7 @@ impl ReactionMeta { ) -> Option { let url = format!( "{}/channels/{}/messages/{}/reactions/{}/{}", - user.belongs_to.borrow().urls.get_api(), + user.belongs_to.borrow().urls.api, self.channel_id, self.message_id, emoji, diff --git a/src/api/guilds/guilds.rs b/src/api/guilds/guilds.rs index 95c3bc2..b23cad1 100644 --- a/src/api/guilds/guilds.rs +++ b/src/api/guilds/guilds.rs @@ -32,7 +32,7 @@ impl Guild { user: &mut UserMeta, guild_create_schema: GuildCreateSchema, ) -> Result { - let url = format!("{}/guilds/", user.belongs_to.borrow().urls.get_api()); + let url = format!("{}/guilds/", user.belongs_to.borrow().urls.api); let request = reqwest::Client::new() .post(url.clone()) .bearer_auth(user.token.clone()) @@ -67,7 +67,7 @@ impl Guild { pub async fn delete(user: &mut UserMeta, guild_id: &str) -> Option { let url = format!( "{}/guilds/{}/delete/", - user.belongs_to.borrow().urls.get_api(), + user.belongs_to.borrow().urls.api, guild_id ); let request = reqwest::Client::new() @@ -97,7 +97,7 @@ impl Guild { let mut belongs_to = user.belongs_to.borrow_mut(); Channel::_create( &user.token, - &format!("{}", belongs_to.urls.get_api()), + &format!("{}", belongs_to.urls.api), &self.id.to_string(), schema, &mut user.limits, @@ -119,7 +119,7 @@ impl Guild { let request = Client::new() .get(format!( "{}/guilds/{}/channels/", - user.belongs_to.borrow().urls.get_api(), + user.belongs_to.borrow().urls.api, self.id )) .bearer_auth(user.token()); @@ -157,7 +157,7 @@ impl Guild { pub async fn get(user: &mut UserMeta, guild_id: &str) -> Result { let mut belongs_to = user.belongs_to.borrow_mut(); Guild::_get( - &format!("{}", belongs_to.urls.get_api()), + &format!("{}", belongs_to.urls.api), guild_id, &user.token, &mut user.limits, @@ -217,7 +217,7 @@ impl Channel { let mut belongs_to = user.belongs_to.borrow_mut(); Channel::_create( &user.token, - &format!("{}", belongs_to.urls.get_api()), + &format!("{}", belongs_to.urls.api), guild_id, schema, &mut user.limits, diff --git a/src/api/guilds/member.rs b/src/api/guilds/member.rs index 4529073..1b3fab3 100644 --- a/src/api/guilds/member.rs +++ b/src/api/guilds/member.rs @@ -26,7 +26,7 @@ impl types::GuildMember { ) -> Result { let url = format!( "{}/guilds/{}/members/{}/", - user.belongs_to.borrow().urls.get_api(), + user.belongs_to.borrow().urls.api, guild_id, member_id ); @@ -59,7 +59,7 @@ impl types::GuildMember { ) -> Option { let url = format!( "{}/guilds/{}/members/{}/roles/{}/", - user.belongs_to.borrow().urls.get_api(), + user.belongs_to.borrow().urls.api, guild_id, member_id, role_id @@ -88,7 +88,7 @@ impl types::GuildMember { ) -> Option { let url = format!( "{}/guilds/{}/members/{}/roles/{}/", - user.belongs_to.borrow().urls.get_api(), + user.belongs_to.borrow().urls.api, guild_id, member_id, role_id diff --git a/src/api/guilds/roles.rs b/src/api/guilds/roles.rs index dd76bf6..f05b7f1 100644 --- a/src/api/guilds/roles.rs +++ b/src/api/guilds/roles.rs @@ -29,7 +29,7 @@ impl types::RoleObject { ) -> Result>, ChorusLibError> { let url = format!( "{}/guilds/{}/roles/", - user.belongs_to.borrow().urls.get_api(), + user.belongs_to.borrow().urls.api, guild_id ); let request = Client::new().get(url).bearer_auth(user.token()); @@ -68,7 +68,7 @@ impl types::RoleObject { ) -> Result { let url = format!( "{}/guilds/{}/roles/{}/", - user.belongs_to.borrow().urls.get_api(), + user.belongs_to.borrow().urls.api, guild_id, role_id ); @@ -98,7 +98,7 @@ impl types::RoleObject { ) -> Result { let url = format!( "{}/guilds/{}/roles/", - user.belongs_to.borrow().urls.get_api(), + user.belongs_to.borrow().urls.api, guild_id ); let body = to_string::(&role_create_schema).map_err(|e| { @@ -132,7 +132,7 @@ impl types::RoleObject { ) -> Result { let url = format!( "{}/guilds/{}/roles/", - user.belongs_to.borrow().urls.get_api(), + user.belongs_to.borrow().urls.api, guild_id ); let body = to_string(&role_position_update_schema).map_err(|e| { @@ -172,7 +172,7 @@ impl types::RoleObject { ) -> Result { let url = format!( "{}/guilds/{}/roles/{}", - user.belongs_to.borrow().urls.get_api(), + user.belongs_to.borrow().urls.api, guild_id, role_id ); diff --git a/src/api/policies/instance/instance.rs b/src/api/policies/instance/instance.rs index c775195..816ab11 100644 --- a/src/api/policies/instance/instance.rs +++ b/src/api/policies/instance/instance.rs @@ -15,7 +15,7 @@ impl Instance { &self, ) -> Result { let client = Client::new(); - let endpoint_url = self.urls.get_api().to_string() + "/policies/instance/"; + let endpoint_url = self.urls.api.clone() + "/policies/instance/"; let request = match client.get(&endpoint_url).send().await { Ok(result) => result, Err(e) => { @@ -33,7 +33,6 @@ impl Instance { } let body = request.text().await.unwrap(); - let instance_policies_schema: GeneralConfiguration = from_str(&body).unwrap(); - Ok(instance_policies_schema) + Ok(from_str::(&body).unwrap()) } } diff --git a/src/api/policies/instance/limits.rs b/src/api/policies/instance/limits.rs index 4ab17f5..3c06d29 100644 --- a/src/api/policies/instance/limits.rs +++ b/src/api/policies/instance/limits.rs @@ -311,7 +311,7 @@ pub mod limits { /// TODO: Change this to return a Result and handle the errors properly. pub async fn check_limits(api_url: String) -> Limits { let client = Client::new(); - let url_parsed = crate::URLBundle::parse_url(api_url) + "/policies/instance/limits"; + let url_parsed = crate::UrlBundle::parse_url(api_url) + "/policies/instance/limits"; let result = client .get(url_parsed) .send() diff --git a/src/api/users/relationships.rs b/src/api/users/relationships.rs index fae5725..7dc34e5 100644 --- a/src/api/users/relationships.rs +++ b/src/api/users/relationships.rs @@ -23,7 +23,7 @@ impl UserMeta { ) -> Result, ChorusLibError> { let url = format!( "{}/users/{}/relationships/", - self.belongs_to.borrow().urls.get_api(), + self.belongs_to.borrow().urls.api, user_id ); let request = Client::new().get(url).bearer_auth(self.token()); @@ -42,7 +42,7 @@ impl UserMeta { pub async fn get_relationships(&mut self) -> Result, ChorusLibError> { let url = format!( "{}/users/@me/relationships/", - self.belongs_to.borrow().urls.get_api() + self.belongs_to.borrow().urls.api ); let request = Client::new().get(url).bearer_auth(self.token()); deserialize_response::>( @@ -67,7 +67,7 @@ impl UserMeta { ) -> Option { let url = format!( "{}/users/@me/relationships/", - self.belongs_to.borrow().urls.get_api() + self.belongs_to.borrow().urls.api ); let body = to_string(&schema).unwrap(); let request = Client::new().post(url).bearer_auth(self.token()).body(body); @@ -140,7 +140,7 @@ impl UserMeta { pub async fn remove_relationship(&mut self, user_id: &str) -> Option { let url = format!( "{}/users/@me/relationships/{}/", - self.belongs_to.borrow().urls.get_api(), + self.belongs_to.borrow().urls.api, user_id ); let request = Client::new().delete(url).bearer_auth(self.token()); diff --git a/src/api/users/users.rs b/src/api/users/users.rs index 2e2485e..cc543ca 100644 --- a/src/api/users/users.rs +++ b/src/api/users/users.rs @@ -52,10 +52,7 @@ impl UserMeta { return Err(ChorusLibError::PasswordRequiredError); } let request = Client::new() - .patch(format!( - "{}/users/@me/", - self.belongs_to.borrow().urls.get_api() - )) + .patch(format!("{}/users/@me/", self.belongs_to.borrow().urls.api)) .body(to_string(&modify_schema).unwrap()) .bearer_auth(self.token()); let user_updated = @@ -79,7 +76,7 @@ impl UserMeta { let request = Client::new() .post(format!( "{}/users/@me/delete/", - self.belongs_to.borrow().urls.get_api() + self.belongs_to.borrow().urls.api )) .bearer_auth(self.token()); handle_request_as_option(request, &mut self, crate::api::limits::LimitType::Ip).await @@ -91,7 +88,7 @@ impl User { let mut belongs_to = user.belongs_to.borrow_mut(); User::_get( &user.token(), - &format!("{}", belongs_to.urls.get_api()), + &format!("{}", belongs_to.urls.api), &mut belongs_to.limits, id, ) @@ -166,6 +163,6 @@ impl Instance { token: String, id: Option<&String>, ) -> Result { - User::_get(&token, self.urls.get_api(), &mut self.limits, id).await + User::_get(&token, &self.urls.api, &mut self.limits, id).await } } diff --git a/src/instance.rs b/src/instance.rs index cc29c99..d33fb95 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -7,14 +7,14 @@ use serde::{Deserialize, Serialize}; use crate::api::limits::Limits; use crate::errors::{ChorusLibError, FieldFormatError}; use crate::types::{GeneralConfiguration, User, UserSettings}; -use crate::URLBundle; +use crate::UrlBundle; #[derive(Debug, Clone)] /** The [`Instance`] what you will be using to perform all sorts of actions on the Spacebar server. */ pub struct Instance { - pub urls: URLBundle, + pub urls: UrlBundle, pub instance_info: GeneralConfiguration, pub limits: Limits, } @@ -26,7 +26,7 @@ impl Instance { /// * `requester` - The [`LimitedRequester`] that will be used to make requests to the Spacebar server. /// # Errors /// * [`InstanceError`] - If the instance cannot be created. - pub async fn new(urls: URLBundle) -> Result { + pub async fn new(urls: UrlBundle) -> Result { let mut instance = Instance { urls: urls.clone(), // Will be overwritten in the next step diff --git a/src/lib.rs b/src/lib.rs index 06cbb81..bfda613 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,18 +18,18 @@ pub mod voice; #[derive(Clone, Default, Debug, PartialEq, Eq)] /// A URLBundle is a struct which bundles together the API-, Gateway- and CDN-URLs of a Spacebar /// instance. -pub struct URLBundle { +pub struct UrlBundle { pub api: String, pub wss: String, pub cdn: String, } -impl URLBundle { +impl UrlBundle { pub fn new(api: String, wss: String, cdn: String) -> Self { Self { - api: URLBundle::parse_url(api), - wss: URLBundle::parse_url(wss), - cdn: URLBundle::parse_url(cdn), + api: UrlBundle::parse_url(api), + wss: UrlBundle::parse_url(wss), + cdn: UrlBundle::parse_url(cdn), } } @@ -44,13 +44,13 @@ impl URLBundle { let url = match Url::parse(&url) { Ok(url) => { if url.scheme() == "localhost" { - return URLBundle::parse_url(format!("http://{}", url)); + return UrlBundle::parse_url(format!("http://{}", url)); } url } Err(ParseError::RelativeUrlWithoutBase) => { let url_fmt = format!("http://{}", url); - return URLBundle::parse_url(url_fmt); + return UrlBundle::parse_url(url_fmt); } Err(_) => panic!("Invalid URL"), }; @@ -61,18 +61,6 @@ impl URLBundle { } url_string } - - pub fn get_api(&self) -> &str { - &self.api - } - - pub fn get_cdn(&self) -> &str { - &self.cdn - } - - pub fn get_wss(&self) -> &str { - &self.wss - } } #[cfg(test)] @@ -81,13 +69,13 @@ mod lib { #[test] fn test_parse_url() { - let mut result = URLBundle::parse_url(String::from("localhost:3000/")); + let mut result = UrlBundle::parse_url(String::from("localhost:3000/")); assert_eq!(result, String::from("http://localhost:3000")); - result = URLBundle::parse_url(String::from("https://some.url.com/")); + result = UrlBundle::parse_url(String::from("https://some.url.com/")); assert_eq!(result, String::from("https://some.url.com")); - result = URLBundle::parse_url(String::from("https://some.url.com/")); + result = UrlBundle::parse_url(String::from("https://some.url.com/")); assert_eq!(result, String::from("https://some.url.com")); - result = URLBundle::parse_url(String::from("https://some.url.com")); + result = UrlBundle::parse_url(String::from("https://some.url.com")); assert_eq!(result, String::from("https://some.url.com")); } } diff --git a/src/limit.rs b/src/limit.rs index 2857449..b9820f8 100644 --- a/src/limit.rs +++ b/src/limit.rs @@ -256,13 +256,13 @@ impl LimitedRequester { mod rate_limit { use serde_json::from_str; - use crate::{api::limits::Config, URLBundle}; + use crate::{api::limits::Config, UrlBundle}; use super::*; #[tokio::test] async fn run_into_limit() { - let urls = URLBundle::new( + let urls = UrlBundle::new( String::from("http://localhost:3001/api/"), String::from("wss://localhost:3001/"), String::from("http://localhost:3001/cdn"), @@ -289,7 +289,7 @@ mod rate_limit { #[tokio::test] async fn test_send_request() { - let urls = URLBundle::new( + let urls = UrlBundle::new( String::from("http://localhost:3001/api/"), String::from("wss://localhost:3001/"), String::from("http://localhost:3001/cdn"), diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 224014a..dc6bcc8 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -4,12 +4,12 @@ use chorus::{ Channel, ChannelCreateSchema, Guild, GuildCreateSchema, RegisterSchema, RegisterSchemaOptions, RoleCreateModifySchema, RoleObject, }, - URLBundle, + UrlBundle, }; #[derive(Debug)] pub struct TestBundle { - pub urls: URLBundle, + pub urls: UrlBundle, pub user: UserMeta, pub instance: Instance, pub guild: Guild, @@ -19,7 +19,7 @@ pub struct TestBundle { // Set up a test by creating an Instance and a User. Reduces Test boilerplate. pub async fn setup() -> TestBundle { - let urls = URLBundle::new( + let urls = UrlBundle::new( "http://localhost:3001/api".to_string(), "ws://localhost:3001".to_string(), "http://localhost:3001".to_string(), From 04c3ee56e94ca41a5117cdb7073822b22eb74593 Mon Sep 17 00:00:00 2001 From: Vincent Junge Date: Tue, 20 Jun 2023 14:42:50 +0200 Subject: [PATCH 32/34] simplify GatewayEvent --- examples/gateway_observers.rs | 11 +-- src/gateway.rs | 171 ++++++++++++---------------------- src/types/events/resume.rs | 2 +- tests/message.rs | 17 ++++ 4 files changed, 80 insertions(+), 121 deletions(-) diff --git a/examples/gateway_observers.rs b/examples/gateway_observers.rs index 6e71751..d8762e0 100644 --- a/examples/gateway_observers.rs +++ b/examples/gateway_observers.rs @@ -4,7 +4,7 @@ use chorus::{ types::{GatewayIdentifyPayload, GatewayReady}, }; use std::{sync::Arc, time::Duration}; -use tokio::{self, sync::Mutex, time::sleep}; +use tokio::{self, time::sleep}; // This example creates a simple gateway connection and a basic observer struct @@ -17,7 +17,7 @@ pub struct ExampleObserver {} // One struct can be an observer of multiple websocketevents, if needed impl Observer for ExampleObserver { // After we subscribe to an event this function is called every time we receive it - fn update(&mut self, _data: &GatewayReady) { + fn update(&self, _data: &GatewayReady) { println!("Observed Ready!"); } } @@ -33,8 +33,8 @@ async fn main() { // Create an instance of our observer let observer = ExampleObserver {}; - // Because observers have to reside in between the main and gateway thread, (they have to be shared between both) we need to put them in an Arc - let shared_observer = Arc::new(Mutex::new(observer)); + // Share ownership of the observer with the gateway + let shared_observer = Arc::new(observer); // Subscribe our observer to the Ready event on this gateway // From now on observer.update(data) will be called every time we receive the Ready event @@ -44,8 +44,7 @@ async fn main() { .await .session .ready - .subscribe(shared_observer) - .unwrap(); + .subscribe(shared_observer); // Authenticate so we will receive any events let token = "SecretToken".to_string(); diff --git a/src/gateway.rs b/src/gateway.rs index 5cececa..7ead0ce 100644 --- a/src/gateway.rs +++ b/src/gateway.rs @@ -3,6 +3,7 @@ use crate::errors::ObserverError; use crate::gateway::events::Events; use crate::types; use crate::types::WebSocketEvent; +use std::any::Any; use std::sync::Arc; use futures_util::stream::SplitSink; @@ -72,11 +73,9 @@ const GATEWAY_LAZY_REQUEST: u8 = 14; /// The amount of time we wait for a heartbeat ack before resending our heartbeat in ms const HEARTBEAT_ACK_TIMEOUT: u128 = 2000; +/// Represents a messsage received from the gateway. This will be either a [GatewayReceivePayload], containing events, or a [GatewayError]. +/// This struct is used internally when handling messages. #[derive(Clone, Debug)] -/** -Represents a messsage received from the gateway. This will be either a [GatewayReceivePayload], containing events, or a [GatewayError]. -This struct is used internally when handling messages. -*/ pub struct GatewayMessage { /// The message we received from the server message: tokio_tungstenite::tungstenite::Message, @@ -148,13 +147,11 @@ impl GatewayMessage { } } +/// Represents a handle to a Gateway connection. A Gateway connection will create observable +/// [`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)] -/** -Represents a handle to a Gateway connection. A Gateway connection will create observable -[`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. - */ pub struct GatewayHandle { pub url: String, pub events: Arc>, @@ -395,7 +392,7 @@ impl Gateway { return Err(data_deserialize_result.err().unwrap()); } - event.update_data(data_deserialize_result.unwrap()).await; + event.notify(data_deserialize_result.unwrap()).await; Ok(()) } @@ -1370,13 +1367,7 @@ impl Gateway { sessions: result.unwrap(), }; - self.events - .lock() - .await - .session - .replace - .update_data(data) - .await; + self.events.lock().await.session.replace.notify(data).await; } "USER_UPDATE" => { let event = &mut self.events.lock().await.user.update; @@ -1532,9 +1523,7 @@ impl Gateway { } } -/** -Handles sending heartbeats to the gateway in another thread - */ +/// Handles sending heartbeats to the gateway in another thread struct HeartbeatHandler { /// The heartbeat interval in milliseconds pub heartbeat_interval: u128, @@ -1669,10 +1658,8 @@ impl HeartbeatHandler { } } -/** -Used for communications between the heartbeat and gateway thread. -Either signifies a sequence number update, a heartbeat ACK or a Heartbeat request by the server -*/ +/// Used for communications between the heartbeat and gateway thread. +/// Either signifies a sequence number update, a heartbeat ACK or a Heartbeat request by the server #[derive(Clone, Copy, Debug)] struct HeartbeatThreadCommunication { /// The opcode for the communication we received, if relevant @@ -1681,89 +1668,47 @@ struct HeartbeatThreadCommunication { sequence_number: Option, } -/** -Trait which defines the behavior of an Observer. An Observer is an object which is subscribed to -an Observable. The Observer is notified when the Observable's data changes. -In this case, the Observable is a [`GatewayEvent`], which is a wrapper around a WebSocketEvent. - */ -pub trait Observer: std::fmt::Debug { - fn update(&mut self, data: &T); +/// Trait which defines the behavior of an Observer. An Observer is an object which is subscribed to +/// an Observable. The Observer is notified when the Observable's data changes. +/// In this case, the Observable is a [`GatewayEvent`], which is a wrapper around a WebSocketEvent. +/// Note that `Debug` is used to tell `Observer`s apart when unsubscribing. +pub trait Observer: Sync + Send + std::fmt::Debug { + fn update(&self, data: &T); } -/** GatewayEvent is a wrapper around a WebSocketEvent. It is used to notify the observers of a -change in the WebSocketEvent. GatewayEvents are observable. - */ +/// GatewayEvent is a wrapper around a WebSocketEvent. It is used to notify the observers of a +/// change in the WebSocketEvent. GatewayEvents are observable. #[derive(Default, Debug)] -pub struct GatewayEvent { - observers: Vec + Sync + Send>>>, - pub event_data: T, - pub is_observed: bool, +pub struct GatewayEvent { + observers: Vec>>, } -impl GatewayEvent { - fn new(event_data: T) -> Self { - Self { - is_observed: false, - observers: Vec::new(), - event_data, - } - } - - /** - Returns true if the GatewayEvent is observed by at least one Observer. - */ +impl GatewayEvent { + /// Returns true if the GatewayEvent is observed by at least one Observer. pub fn is_observed(&self) -> bool { - self.is_observed + !self.observers.is_empty() } - /** - Subscribes an Observer to the GatewayEvent. Returns an error if the GatewayEvent is already - observed. - # Errors - Returns an error if the GatewayEvent is already observed. - Error type: [`ObserverError::AlreadySubscribedError`] - */ - pub fn subscribe( - &mut self, - observable: Arc + Sync + Send>>, - ) -> Result<(), ObserverError> { - if self.is_observed { - return Err(ObserverError::AlreadySubscribedError); - } - self.is_observed = true; + /// Subscribes an Observer to the GatewayEvent. + pub fn subscribe(&mut self, observable: Arc>) { self.observers.push(observable); - Ok(()) } - /** - Unsubscribes an Observer from the GatewayEvent. - */ - pub fn unsubscribe(&mut self, observable: Arc + Sync + Send>>) { + /// Unsubscribes an Observer from the GatewayEvent. + pub fn unsubscribe(&mut self, observable: &dyn Observer) { // .retain()'s closure retains only those elements of the vector, which have a different // pointer value than observable. // The usage of the debug format to compare the generic T of observers is quite stupid, but the only thing to compare between them is T and if T == T they are the same // anddd there is no way to do that without using format + let to_remove = format!("{:?}", observable); self.observers - .retain(|obs| format!("{:?}", obs) != format!("{:?}", &observable)); - self.is_observed = !self.observers.is_empty(); + .retain(|obs| format!("{:?}", obs) != to_remove); } - /** - Updates the GatewayEvent's data and notifies the observers. - */ - async fn update_data(&mut self, new_event_data: T) { - self.event_data = new_event_data; - self.notify().await; - } - - /** - Notifies the observers of the GatewayEvent. - */ - async fn notify(&self) { + /// Notifies the observers of the GatewayEvent. + async fn notify(&self, new_event_data: T) { for observer in &self.observers { - let mut observer_lock = observer.lock().await; - observer_lock.update(&self.event_data); - drop(observer_lock); + observer.update(&new_event_data); } } } @@ -1932,23 +1877,23 @@ mod events { #[cfg(test)] mod example { use super::*; + use std::sync::atomic::{AtomicI32, Ordering::Relaxed}; #[derive(Debug)] - struct Consumer; + struct Consumer { + name: String, + events_received: AtomicI32, + } impl Observer for Consumer { - fn update(&mut self, data: &types::GatewayResume) { - println!("{}", data.token) + fn update(&self, _data: &types::GatewayResume) { + self.events_received.fetch_add(1, Relaxed); } } #[tokio::test] async fn test_observer_behavior() { - let mut event = GatewayEvent::new(types::GatewayResume { - token: "start".to_string(), - session_id: "start".to_string(), - seq: "start".to_string(), - }); + let mut event = GatewayEvent::default(); let new_data = types::GatewayResume { token: "token_3276ha37am3".to_string(), @@ -1956,25 +1901,23 @@ mod example { seq: "3".to_string(), }; - let consumer = Consumer; - let arc_mut_consumer = Arc::new(Mutex::new(consumer)); + let consumer = Arc::new(Consumer { + name: "first".into(), + events_received: 0.into(), + }); + event.subscribe(consumer.clone()); - event.subscribe(arc_mut_consumer.clone()).unwrap(); + let second_consumer = Arc::new(Consumer { + name: "second".into(), + events_received: 0.into(), + }); + event.subscribe(second_consumer.clone()); - event.notify().await; + event.notify(new_data.clone()).await; + event.unsubscribe(&*consumer); + event.notify(new_data).await; - event.update_data(new_data).await; - - let second_consumer = Consumer; - let arc_mut_second_consumer = Arc::new(Mutex::new(second_consumer)); - - match event.subscribe(arc_mut_second_consumer.clone()).err() { - None => panic!(), - Some(err) => println!("You cannot subscribe twice: {}", err), - } - - event.unsubscribe(arc_mut_consumer.clone()); - - event.subscribe(arc_mut_second_consumer).unwrap(); + assert_eq!(consumer.events_received.load(Relaxed), 1); + assert_eq!(second_consumer.events_received.load(Relaxed), 2); } } diff --git a/src/types/events/resume.rs b/src/types/events/resume.rs index 362de98..45d2235 100644 --- a/src/types/events/resume.rs +++ b/src/types/events/resume.rs @@ -1,7 +1,7 @@ use crate::types::events::WebSocketEvent; use serde::{Deserialize, Serialize}; -#[derive(Debug, Deserialize, Serialize, Default)] +#[derive(Debug, Clone, Deserialize, Serialize, Default)] pub struct GatewayResume { pub token: String, pub session_id: String, diff --git a/tests/message.rs b/tests/message.rs index e92c35c..7f1da9d 100644 --- a/tests/message.rs +++ b/tests/message.rs @@ -64,3 +64,20 @@ async fn send_message_attachment() { .unwrap(); common::teardown(bundle).await } + +#[tokio::test] +async fn read_messages() { + let mut bundle = common::setup().await; + + // First create some messages to read + let mut message = types::MessageSendSchema { + content: Some("A Message!".to_string()), + ..Default::default() + }; + let _ = bundle + .user + .send_message(&mut message, bundle.channel.id.to_string(), None) + .await + .unwrap(); + common::teardown(bundle).await +} From b8d7030ab129f131de4fee23af8d3aefa9ba94e7 Mon Sep 17 00:00:00 2001 From: kozabrada123 <59031733+kozabrada123@users.noreply.github.com> Date: Tue, 20 Jun 2023 18:26:30 +0200 Subject: [PATCH 33/34] Change Option to Result<(), ChorusLibError> --- src/api/channels/channels.rs | 9 ++--- src/api/channels/permissions.rs | 26 +++++--------- src/api/channels/reactions.rs | 61 +++++++++++++-------------------- src/api/common.rs | 12 +++---- src/api/guilds/guilds.rs | 8 ++--- src/api/guilds/member.rs | 14 ++++---- src/api/users/relationships.rs | 26 +++++++------- src/api/users/users.rs | 8 ++--- 8 files changed, 70 insertions(+), 94 deletions(-) diff --git a/src/api/channels/channels.rs b/src/api/channels/channels.rs index e0ede6a..290e240 100644 --- a/src/api/channels/channels.rs +++ b/src/api/channels/channels.rs @@ -42,8 +42,8 @@ impl Channel { /// /// # Returns /// - /// An `Option` that contains an `ChorusLibError` if an error occurred during the request, or `None` if the request was successful. - pub async fn delete(self, user: &mut UserMeta) -> Option { + /// A `Result` that contains a `ChorusLibError` if an error occurred during the request, or `()` if the request was successful. + pub async fn delete(self, user: &mut UserMeta) -> Result<(), ChorusLibError> { let request = Client::new() .delete(format!( "{}/channels/{}/", @@ -52,8 +52,9 @@ impl Channel { )) .bearer_auth(user.token()); let response = - common::handle_request(request, user, crate::api::limits::LimitType::Channel).await; - response.err() + common::handle_request_as_result(request, user, crate::api::limits::LimitType::Channel) + .await; + response } /// Modifies a channel. diff --git a/src/api/channels/permissions.rs b/src/api/channels/permissions.rs index 7871479..263b3d1 100644 --- a/src/api/channels/permissions.rs +++ b/src/api/channels/permissions.rs @@ -2,7 +2,7 @@ use reqwest::Client; use serde_json::to_string; use crate::{ - api::handle_request, + api::{handle_request, handle_request_as_result}, errors::ChorusLibError, instance::UserMeta, types::{self, PermissionOverwrite}, @@ -19,12 +19,12 @@ impl types::Channel { /// /// # Returns /// - /// This function returns [`None`] if the request is successful, otherwise it returns a [`ChorusLibError`] instance. + /// This function returns a result that is either [`Ok(())`] if the request is successful, or an [`Err(ChorusLibError)`]. pub async fn edit_permissions( user: &mut UserMeta, channel_id: &str, overwrite: PermissionOverwrite, - ) -> Option { + ) -> Result<(), ChorusLibError> { let url = { format!( "{}/channels/{}/permissions/{}", @@ -36,18 +36,13 @@ impl types::Channel { let body = match to_string(&overwrite) { Ok(string) => string, Err(e) => { - return Some(ChorusLibError::FormCreationError { + return Err(ChorusLibError::FormCreationError { error: e.to_string(), }); } }; let request = Client::new().put(url).bearer_auth(user.token()).body(body); - match handle_request(request, user, crate::api::limits::LimitType::Channel).await { - Ok(_) => None, - Err(e) => Some(ChorusLibError::InvalidResponseError { - error: e.to_string(), - }), - } + handle_request_as_result(request, user, crate::api::limits::LimitType::Channel).await } /// Deletes a permission overwrite for a channel. @@ -60,12 +55,12 @@ impl types::Channel { /// /// # Returns /// - /// This function returns [`None`] if the request is successful, otherwise it returns a [`ChorusLibError`] instance. + /// This function returns a Result that is either [`Ok(())`] if the request is successfulm or an [`Err(ChorusLibError)`]. pub async fn delete_permission( user: &mut UserMeta, channel_id: &str, overwrite_id: &str, - ) -> Option { + ) -> Result<(), ChorusLibError> { let url = format!( "{}/channels/{}/permissions/{}", user.belongs_to.borrow_mut().urls.api, @@ -73,11 +68,6 @@ impl types::Channel { overwrite_id ); let request = Client::new().delete(url).bearer_auth(user.token()); - match handle_request(request, user, crate::api::limits::LimitType::Channel).await { - Ok(_) => None, - Err(e) => Some(ChorusLibError::InvalidResponseError { - error: e.to_string(), - }), - } + handle_request_as_result(request, user, crate::api::limits::LimitType::Channel).await } } diff --git a/src/api/channels/reactions.rs b/src/api/channels/reactions.rs index dfd1838..77b7e32 100644 --- a/src/api/channels/reactions.rs +++ b/src/api/channels/reactions.rs @@ -1,7 +1,7 @@ use reqwest::Client; use crate::{ - api::{handle_request, handle_request_as_option}, + api::{handle_request, handle_request_as_result}, errors::ChorusLibError, instance::UserMeta, types, @@ -24,13 +24,13 @@ impl ReactionMeta { * `user` - A mutable reference to a [`UserMeta`] instance. # Returns - An `Option` [`crate::errors::ChorusLibError`] if something went wrong. + A `Result` [`()`] [`crate::errors::ChorusLibError`] if something went wrong. Fires a `Message Reaction Remove All` Gateway event. # Reference See [https://discord.com/developers/docs/resources/channel#delete-all-reactions](https://discord.com/developers/docs/resources/channel#delete-all-reactions) */ - pub async fn delete_all(&self, user: &mut UserMeta) -> Option { + pub async fn delete_all(&self, user: &mut UserMeta) -> Result<(), ChorusLibError> { let url = format!( "{}/channels/{}/messages/{}/reactions/", user.belongs_to.borrow().urls.api, @@ -38,12 +38,7 @@ impl ReactionMeta { self.message_id ); let request = Client::new().delete(url).bearer_auth(user.token()); - match handle_request(request, user, crate::api::limits::LimitType::Channel).await { - Ok(_) => None, - Err(e) => Some(ChorusLibError::InvalidResponseError { - error: e.to_string(), - }), - } + handle_request_as_result(request, user, crate::api::limits::LimitType::Channel).await } /** @@ -56,12 +51,12 @@ impl ReactionMeta { * `user` - A mutable reference to a [`UserMeta`] instance. # Returns - A [`crate::errors::ChorusLibError`] if something went wrong. + A Result that is [`Err(crate::errors::ChorusLibError)`] if something went wrong. # Reference See [https://discord.com/developers/docs/resources/channel#get-reactions](https://discord.com/developers/docs/resources/channel#get-reactions) */ - pub async fn get(&self, emoji: &str, user: &mut UserMeta) -> Option { + pub async fn get(&self, emoji: &str, user: &mut UserMeta) -> Result<(), ChorusLibError> { let url = format!( "{}/channels/{}/messages/{}/reactions/{}/", user.belongs_to.borrow().urls.api, @@ -70,12 +65,7 @@ impl ReactionMeta { emoji ); let request = Client::new().get(url).bearer_auth(user.token()); - match handle_request(request, user, crate::api::limits::LimitType::Channel).await { - Ok(_) => None, - Err(e) => Some(ChorusLibError::InvalidResponseError { - error: e.to_string(), - }), - } + handle_request_as_result(request, user, crate::api::limits::LimitType::Channel).await } /** @@ -89,13 +79,17 @@ impl ReactionMeta { * `user` - A mutable reference to a [`UserMeta`] instance. # Returns - A [`crate::errors::ChorusLibError`] if something went wrong. + A Result that is [`Err(crate::errors::ChorusLibError)`] if something went wrong. Fires a `Message Reaction Remove Emoji` Gateway event. # Reference See [https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji](https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji) */ - pub async fn delete_emoji(&self, emoji: &str, user: &mut UserMeta) -> Option { + pub async fn delete_emoji( + &self, + emoji: &str, + user: &mut UserMeta, + ) -> Result<(), ChorusLibError> { let url = format!( "{}/channels/{}/messages/{}/reactions/{}/", user.belongs_to.borrow().urls.api, @@ -104,12 +98,7 @@ impl ReactionMeta { emoji ); let request = Client::new().delete(url).bearer_auth(user.token()); - match handle_request(request, user, crate::api::limits::LimitType::Channel).await { - Ok(_) => None, - Err(e) => Some(ChorusLibError::InvalidResponseError { - error: e.to_string(), - }), - } + handle_request_as_result(request, user, crate::api::limits::LimitType::Channel).await } /** @@ -126,14 +115,12 @@ impl ReactionMeta { * `user` - A mutable reference to a [`UserMeta`] instance. # Returns - A `Result` containing a [`reqwest::Response`] or a [`crate::errors::ChorusLibError`]. - Returns a 204 empty response on success. - Fires a Message Reaction Add Gateway event. + A `Result` containing [`()`] or a [`crate::errors::ChorusLibError`]. # Reference See [https://discord.com/developers/docs/resources/channel#create-reaction](https://discord.com/developers/docs/resources/channel#create-reaction) */ - pub async fn create(&self, emoji: &str, user: &mut UserMeta) -> Option { + pub async fn create(&self, emoji: &str, user: &mut UserMeta) -> Result<(), ChorusLibError> { let url = format!( "{}/channels/{}/messages/{}/reactions/{}/@me/", user.belongs_to.borrow().urls.api, @@ -142,7 +129,7 @@ impl ReactionMeta { emoji ); let request = Client::new().put(url).bearer_auth(user.token()); - handle_request_as_option(request, user, crate::api::limits::LimitType::Channel).await + handle_request_as_result(request, user, crate::api::limits::LimitType::Channel).await } /** @@ -155,14 +142,13 @@ impl ReactionMeta { * `user` - A mutable reference to a [`UserMeta`] instance. # Returns - A `Result` containing a [`reqwest::Response`] or a [`crate::errors::ChorusLibError`]. - Returns a 204 empty response on success. + A `Result` containing [`()`] or a [`crate::errors::ChorusLibError`]. Fires a `Message Reaction Remove` Gateway event. # Reference See [https://discord.com/developers/docs/resources/channel#delete-own-reaction](https://discord.com/developers/docs/resources/channel#delete-own-reaction) */ - pub async fn remove(&self, emoji: &str, user: &mut UserMeta) -> Option { + pub async fn remove(&self, emoji: &str, user: &mut UserMeta) -> Result<(), ChorusLibError> { let url = format!( "{}/channels/{}/messages/{}/reactions/{}/@me/", user.belongs_to.borrow().urls.api, @@ -171,7 +157,7 @@ impl ReactionMeta { emoji ); let request = Client::new().delete(url).bearer_auth(user.token()); - handle_request_as_option(request, user, crate::api::limits::LimitType::Channel).await + handle_request_as_result(request, user, crate::api::limits::LimitType::Channel).await } /** @@ -187,8 +173,7 @@ impl ReactionMeta { * `user` - A mutable reference to a [`UserMeta`] instance. # Returns - A `Result` containing a [`reqwest::Response`] or a [`crate::errors::ChorusLibError`]. - Returns a 204 empty response on success. + A `Result` containing [`()`] or a [`crate::errors::ChorusLibError`]. Fires a Message Reaction Remove Gateway event. # Reference @@ -199,7 +184,7 @@ impl ReactionMeta { user_id: &str, emoji: &str, user: &mut UserMeta, - ) -> Option { + ) -> Result<(), ChorusLibError> { let url = format!( "{}/channels/{}/messages/{}/reactions/{}/{}", user.belongs_to.borrow().urls.api, @@ -209,6 +194,6 @@ impl ReactionMeta { user_id ); let request = Client::new().delete(url).bearer_auth(user.token()); - handle_request_as_option(request, user, crate::api::limits::LimitType::Channel).await + handle_request_as_result(request, user, crate::api::limits::LimitType::Channel).await } } diff --git a/src/api/common.rs b/src/api/common.rs index af9d975..f61ab19 100644 --- a/src/api/common.rs +++ b/src/api/common.rs @@ -21,16 +21,16 @@ pub async fn handle_request( .await } -/// Sends a request to wherever it needs to go. Returns [`None`] on success and -/// [`Some(ChorusLibError)`] on failure. -pub async fn handle_request_as_option( +/// Sends a request to wherever it needs to go. Returns [`Ok(())`] on success and +/// [`Err(ChorusLibError)`] on failure. +pub async fn handle_request_as_result( request: RequestBuilder, user: &mut UserMeta, limit_type: LimitType, -) -> Option { +) -> Result<(), ChorusLibError> { match handle_request(request, user, limit_type).await { - Ok(_) => None, - Err(e) => Some(ChorusLibError::InvalidResponseError { + Ok(_) => Ok(()), + Err(e) => Err(ChorusLibError::InvalidResponseError { error: e.to_string(), }), } diff --git a/src/api/guilds/guilds.rs b/src/api/guilds/guilds.rs index b23cad1..05bf8c2 100644 --- a/src/api/guilds/guilds.rs +++ b/src/api/guilds/guilds.rs @@ -4,7 +4,7 @@ use serde_json::to_string; use crate::api::deserialize_response; use crate::api::handle_request; -use crate::api::handle_request_as_option; +use crate::api::handle_request_as_result; use crate::api::limits::Limits; use crate::errors::ChorusLibError; use crate::instance::UserMeta; @@ -50,7 +50,7 @@ impl Guild { /// /// # Returns /// - /// An `Option` containing an `ChorusLibError` if an error occurred during the request, otherwise `None`. + /// An `Result` containing an `ChorusLibError` if an error occurred during the request, otherwise `()`. /// /// # Example /// @@ -64,7 +64,7 @@ impl Guild { /// None => println!("Guild deleted successfully"), /// } /// ``` - pub async fn delete(user: &mut UserMeta, guild_id: &str) -> Option { + pub async fn delete(user: &mut UserMeta, guild_id: &str) -> Result<(), ChorusLibError> { let url = format!( "{}/guilds/{}/delete/", user.belongs_to.borrow().urls.api, @@ -73,7 +73,7 @@ impl Guild { let request = reqwest::Client::new() .post(url.clone()) .bearer_auth(user.token.clone()); - handle_request_as_option(request, user, crate::api::limits::LimitType::Guild).await + handle_request_as_result(request, user, crate::api::limits::LimitType::Guild).await } /// Sends a request to create a new channel in the guild. diff --git a/src/api/guilds/member.rs b/src/api/guilds/member.rs index 1b3fab3..7b18aa7 100644 --- a/src/api/guilds/member.rs +++ b/src/api/guilds/member.rs @@ -1,7 +1,7 @@ use reqwest::Client; use crate::{ - api::{deserialize_response, handle_request_as_option}, + api::{deserialize_response, handle_request_as_result}, errors::ChorusLibError, instance::UserMeta, types, @@ -50,13 +50,13 @@ impl types::GuildMember { /// /// # Returns /// - /// An `Option` containing a `ChorusLibError` if the request fails, or `None` if the request succeeds. + /// An `Result` containing a `ChorusLibError` if the request fails, or `()` if the request succeeds. pub async fn add_role( user: &mut UserMeta, guild_id: &str, member_id: &str, role_id: &str, - ) -> Option { + ) -> Result<(), ChorusLibError> { let url = format!( "{}/guilds/{}/members/{}/roles/{}/", user.belongs_to.borrow().urls.api, @@ -65,7 +65,7 @@ impl types::GuildMember { role_id ); let request = Client::new().put(url).bearer_auth(user.token()); - handle_request_as_option(request, user, crate::api::limits::LimitType::Guild).await + handle_request_as_result(request, user, crate::api::limits::LimitType::Guild).await } /// Removes a role from a guild member. @@ -79,13 +79,13 @@ impl types::GuildMember { /// /// # Returns /// - /// An `Option` containing a `ChorusLibError` if the request fails, or `None` if the request succeeds. + /// A `Result` containing a `ChorusLibError` if the request fails, or `()` if the request succeeds. pub async fn remove_role( user: &mut UserMeta, guild_id: &str, member_id: &str, role_id: &str, - ) -> Option { + ) -> Result<(), crate::errors::ChorusLibError> { let url = format!( "{}/guilds/{}/members/{}/roles/{}/", user.belongs_to.borrow().urls.api, @@ -94,6 +94,6 @@ impl types::GuildMember { role_id ); let request = Client::new().delete(url).bearer_auth(user.token()); - handle_request_as_option(request, user, crate::api::limits::LimitType::Guild).await + handle_request_as_result(request, user, crate::api::limits::LimitType::Guild).await } } diff --git a/src/api/users/relationships.rs b/src/api/users/relationships.rs index 7dc34e5..2584ff5 100644 --- a/src/api/users/relationships.rs +++ b/src/api/users/relationships.rs @@ -2,7 +2,7 @@ use reqwest::Client; use serde_json::to_string; use crate::{ - api::{deserialize_response, handle_request_as_option}, + api::{deserialize_response, handle_request_as_result}, errors::ChorusLibError, instance::UserMeta, types::{self, CreateUserRelationshipSchema, RelationshipType}, @@ -60,18 +60,18 @@ impl UserMeta { /// * `schema` - A [`FriendRequestSendSchema`] struct that holds the information about the friend request to be sent. /// /// # Returns - /// This function returns an [`Option`] that holds a [`ChorusLibError`] if the request fails. + /// This function returns a [`Result`] that holds a [`ChorusLibError`] if the request fails. pub async fn send_friend_request( &mut self, schema: types::FriendRequestSendSchema, - ) -> Option { + ) -> Result<(), ChorusLibError> { let url = format!( "{}/users/@me/relationships/", self.belongs_to.borrow().urls.api ); let body = to_string(&schema).unwrap(); let request = Client::new().post(url).bearer_auth(self.token()).body(body); - handle_request_as_option(request, self, crate::api::limits::LimitType::Global).await + handle_request_as_result(request, self, crate::api::limits::LimitType::Global).await } /// Modifies the relationship between the authenticated user and the specified user. @@ -87,19 +87,19 @@ impl UserMeta { /// * [`RelationshipType::Blocked`]: Blocks the specified user_id. /// /// # Returns - /// This function returns an [`Option`] that holds a [`ChorusLibError`] if the request fails. + /// This function returns an [`Result`] that holds a [`ChorusLibError`] if the request fails. pub async fn modify_user_relationship( &mut self, user_id: &str, relationship_type: RelationshipType, - ) -> Option { + ) -> Result<(), ChorusLibError> { let api_url = self.belongs_to.borrow().urls.api.clone(); match relationship_type { RelationshipType::None => { let request = Client::new() .delete(format!("{}/users/@me/relationships/{}/", api_url, user_id)) .bearer_auth(self.token()); - handle_request_as_option(request, self, crate::api::limits::LimitType::Global).await + handle_request_as_result(request, self, crate::api::limits::LimitType::Global).await } RelationshipType::Friends | RelationshipType::Incoming | RelationshipType::Outgoing => { let body = CreateUserRelationshipSchema { @@ -111,7 +111,7 @@ impl UserMeta { .put(format!("{}/users/@me/relationships/{}/", api_url, user_id)) .bearer_auth(self.token()) .body(to_string(&body).unwrap()); - handle_request_as_option(request, self, crate::api::limits::LimitType::Global).await + handle_request_as_result(request, self, crate::api::limits::LimitType::Global).await } RelationshipType::Blocked => { let body = CreateUserRelationshipSchema { @@ -123,9 +123,9 @@ impl UserMeta { .put(format!("{}/users/@me/relationships/{}/", api_url, user_id)) .bearer_auth(self.token()) .body(to_string(&body).unwrap()); - handle_request_as_option(request, self, crate::api::limits::LimitType::Global).await + handle_request_as_result(request, self, crate::api::limits::LimitType::Global).await } - RelationshipType::Suggestion | RelationshipType::Implicit => None, + RelationshipType::Suggestion | RelationshipType::Implicit => Ok(()), } } @@ -136,14 +136,14 @@ impl UserMeta { /// * `user_id` - A string slice that holds the ID of the user to remove the relationship with. /// /// # Returns - /// This function returns an [`Option`] that holds a [`ChorusLibError`] if the request fails. - pub async fn remove_relationship(&mut self, user_id: &str) -> Option { + /// This function returns a [`Result`] that holds a [`ChorusLibError`] if the request fails. + pub async fn remove_relationship(&mut self, user_id: &str) -> Result<(), ChorusLibError> { let url = format!( "{}/users/@me/relationships/{}/", self.belongs_to.borrow().urls.api, user_id ); let request = Client::new().delete(url).bearer_auth(self.token()); - handle_request_as_option(request, self, crate::api::limits::LimitType::Global).await + handle_request_as_result(request, self, crate::api::limits::LimitType::Global).await } } diff --git a/src/api/users/users.rs b/src/api/users/users.rs index cc543ca..5bd509b 100644 --- a/src/api/users/users.rs +++ b/src/api/users/users.rs @@ -2,7 +2,7 @@ use reqwest::Client; use serde_json::to_string; use crate::{ - api::{deserialize_response, handle_request_as_option, limits::Limits}, + api::{deserialize_response, handle_request_as_result, limits::Limits}, errors::ChorusLibError, instance::{Instance, UserMeta}, limit::LimitedRequester, @@ -71,15 +71,15 @@ impl UserMeta { /// /// # Returns /// - /// Returns `None` if the user was successfully deleted, or an `ChorusLibError` if an error occurred. - pub async fn delete(mut self) -> Option { + /// Returns `()` if the user was successfully deleted, or a `ChorusLibError` if an error occurred. + pub async fn delete(mut self) -> Result<(), ChorusLibError> { let request = Client::new() .post(format!( "{}/users/@me/delete/", self.belongs_to.borrow().urls.api )) .bearer_auth(self.token()); - handle_request_as_option(request, &mut self, crate::api::limits::LimitType::Ip).await + handle_request_as_result(request, &mut self, crate::api::limits::LimitType::Ip).await } } From f2d3aa62e2766c1336a052617b995bd292ba0c05 Mon Sep 17 00:00:00 2001 From: kozabrada123 <59031733+kozabrada123@users.noreply.github.com> Date: Tue, 20 Jun 2023 18:33:37 +0200 Subject: [PATCH 34/34] Update in tests too --- tests/channel.rs | 8 +++++--- tests/guild.rs | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/channel.rs b/tests/channel.rs index bd519b0..d73a318 100644 --- a/tests/channel.rs +++ b/tests/channel.rs @@ -21,7 +21,7 @@ async fn get_channel() { async fn delete_channel() { let mut bundle = common::setup().await; let result = bundle.channel.clone().delete(&mut bundle.user).await; - assert!(result.is_none()); + assert!(result.is_ok()); common::teardown(bundle).await } @@ -72,14 +72,16 @@ async fn modify_channel() { bundle.channel.id.to_string().as_str(), permission_override.clone(), ) - .await; + .await + .unwrap(); Channel::delete_permission( &mut bundle.user, bundle.channel.id.to_string().as_str(), &permission_override.id, ) - .await; + .await + .unwrap(); common::teardown(bundle).await } diff --git a/tests/guild.rs b/tests/guild.rs index 903b1eb..66d7fa7 100644 --- a/tests/guild.rs +++ b/tests/guild.rs @@ -22,7 +22,7 @@ async fn guild_creation_deletion() { assert!(Guild::delete(&mut bundle.user, &guild.id.to_string()) .await - .is_none()); + .is_ok()); common::teardown(bundle).await }