From 6cc92b877e2873f940245317a2f12d8f32aac505 Mon Sep 17 00:00:00 2001
From: Paul <paulmakles@gmail.com>
Date: Fri, 14 May 2021 22:29:43 +0100
Subject: [PATCH] Fix block user route, send correct user struct. Add route for
 fetching members. Cargo fmt on accident.

---
 src/bin/dummy.rs                     |  2 +-
 src/database/entities/channel.rs     | 12 +++----
 src/database/entities/january.rs     | 23 +++++++------
 src/database/entities/message.rs     | 19 +++++------
 src/database/entities/mod.rs         |  8 ++---
 src/database/entities/user.rs        | 50 ++++++++++++++++++++++++++--
 src/main.rs                          |  7 ++--
 src/notifications/events.rs          | 12 ++++---
 src/notifications/payload.rs         | 47 ++++----------------------
 src/routes/channels/edit_channel.rs  | 39 +++++++++++-----------
 src/routes/channels/fetch_members.rs | 23 +++++++++++++
 src/routes/channels/message_query.rs | 10 ++++--
 src/routes/channels/mod.rs           |  2 ++
 src/routes/users/add_friend.rs       | 12 +++----
 src/routes/users/block_user.rs       | 14 ++++----
 src/routes/users/change_username.rs  |  2 +-
 src/routes/users/edit_user.rs        | 25 ++++++++------
 src/routes/users/remove_friend.rs    |  4 +--
 src/routes/users/unblock_user.rs     |  6 ++--
 19 files changed, 184 insertions(+), 133 deletions(-)
 create mode 100644 src/routes/channels/fetch_members.rs

diff --git a/src/bin/dummy.rs b/src/bin/dummy.rs
index e71fdf5..f328e4d 100644
--- a/src/bin/dummy.rs
+++ b/src/bin/dummy.rs
@@ -1 +1 @@
-fn main() {}
\ No newline at end of file
+fn main() {}
diff --git a/src/database/entities/channel.rs b/src/database/entities/channel.rs
index 7f61b70..704d743 100644
--- a/src/database/entities/channel.rs
+++ b/src/database/entities/channel.rs
@@ -44,7 +44,7 @@ pub enum Channel {
         owner: String,
         description: String,
         recipients: Vec<String>,
-        
+
         #[serde(skip_serializing_if = "Option::is_none")]
         icon: Option<File>,
         #[serde(skip_serializing_if = "Option::is_none")]
@@ -93,8 +93,7 @@ impl Channel {
             })?;
 
         let channel_id = self.id().to_string();
-        ClientboundNotification::ChannelCreate(self)
-            .publish(channel_id);
+        ClientboundNotification::ChannelCreate(self).publish(channel_id);
 
         Ok(())
     }
@@ -104,7 +103,7 @@ impl Channel {
         ClientboundNotification::ChannelUpdate {
             id: id.clone(),
             data,
-            clear: None
+            clear: None,
         }
         .publish(id);
 
@@ -188,9 +187,8 @@ impl Channel {
                 with: "channel",
             })?;
 
-        ClientboundNotification::ChannelDelete { id: id.to_string() }
-            .publish(id.to_string());
-        
+        ClientboundNotification::ChannelDelete { id: id.to_string() }.publish(id.to_string());
+
         if let Channel::Group { icon, .. } = self {
             if let Some(attachment) = icon {
                 attachment.delete().await?;
diff --git a/src/database/entities/january.rs b/src/database/entities/january.rs
index c530b75..cb6f9ff 100644
--- a/src/database/entities/january.rs
+++ b/src/database/entities/january.rs
@@ -1,6 +1,9 @@
-use serde::{Serialize, Deserialize};
+use crate::util::{
+    result::{Error, Result},
+    variables::JANUARY_URL,
+};
 use linkify::{LinkFinder, LinkKind};
-use crate::util::{result::{Error, Result}, variables::JANUARY_URL};
+use serde::{Deserialize, Serialize};
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
 pub enum ImageSize {
@@ -33,7 +36,7 @@ pub enum TwitchType {
 #[derive(Serialize, Deserialize, Debug, Clone)]
 pub enum BandcampType {
     Album,
-    Track
+    Track,
 }
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
@@ -54,8 +57,8 @@ pub enum Special {
     Soundcloud,
     Bandcamp {
         content_type: BandcampType,
-        id: String
-    }
+        id: String,
+    },
 }
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
@@ -98,7 +101,7 @@ impl Embed {
                 // Ignore quoted lines.
                 if let Some(c) = v.chars().next() {
                     if c == '>' {
-                        return ""
+                        return "";
                     }
                 }
 
@@ -131,12 +134,10 @@ impl Embed {
             Err(_) => return Err(Error::LabelMe),
             Ok(result) => match result.status() {
                 reqwest::StatusCode::OK => {
-                    let res: Embed = result.json()
-                        .await
-                        .map_err(|_| Error::InvalidOperation)?;
+                    let res: Embed = result.json().await.map_err(|_| Error::InvalidOperation)?;
 
-                    Ok(vec![ res ])
-                },
+                    Ok(vec![res])
+                }
                 _ => return Err(Error::LabelMe),
             },
         }
diff --git a/src/database/entities/message.rs b/src/database/entities/message.rs
index 5e23d41..cca5b28 100644
--- a/src/database/entities/message.rs
+++ b/src/database/entities/message.rs
@@ -135,9 +135,7 @@ impl Message {
         self.process_embed();
 
         let enc = serde_json::to_string(&self).unwrap();
-        ClientboundNotification::Message(self)
-            .publish(channel.id().to_string());
-        
+        ClientboundNotification::Message(self).publish(channel.id().to_string());
 
         /*
            Web Push Test Code
@@ -202,9 +200,11 @@ impl Message {
 
                     for subscription in subscriptions {
                         let mut builder = WebPushMessageBuilder::new(&subscription).unwrap();
-                        let sig_builder =
-                            VapidSignatureBuilder::from_pem(std::io::Cursor::new(&key), &subscription)
-                                .unwrap();
+                        let sig_builder = VapidSignatureBuilder::from_pem(
+                            std::io::Cursor::new(&key),
+                            &subscription,
+                        )
+                        .unwrap();
                         let signature = sig_builder.build().unwrap();
                         builder.set_vapid_signature(signature);
                         builder.set_payload(ContentEncoding::AesGcm, enc.as_bytes());
@@ -250,12 +250,11 @@ impl Message {
                                 },
                                 None,
                             )
-                            .await {
+                            .await
+                        {
                             ClientboundNotification::MessageUpdate {
                                 id,
-                                data: json!({
-                                    "embeds": embeds
-                                }),
+                                data: json!({ "embeds": embeds }),
                             }
                             .publish(channel);
                         }
diff --git a/src/database/entities/mod.rs b/src/database/entities/mod.rs
index 49ea91b..d384f5d 100644
--- a/src/database/entities/mod.rs
+++ b/src/database/entities/mod.rs
@@ -1,13 +1,13 @@
-mod server;
 mod autumn;
-mod january;
 mod channel;
+mod january;
 mod message;
+mod server;
 mod user;
 
-pub use january::*;
 pub use autumn::*;
 pub use channel::*;
-pub use server::*;
+pub use january::*;
 pub use message::*;
+pub use server::*;
 pub use user::*;
diff --git a/src/database/entities/user.rs b/src/database/entities/user.rs
index 96db17d..48befa4 100644
--- a/src/database/entities/user.rs
+++ b/src/database/entities/user.rs
@@ -1,12 +1,16 @@
-use mongodb::bson::doc;
+use futures::StreamExt;
 use mongodb::options::{Collation, FindOneOptions};
+use mongodb::{
+    bson::{doc, from_document},
+    options::FindOptions,
+};
 use serde::{Deserialize, Serialize};
+use validator::Validate;
 
 use crate::database::permissions::user::UserPermissions;
 use crate::database::*;
 use crate::notifications::websocket::is_online;
 use crate::util::result::{Error, Result};
-use validator::Validate;
 
 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
 pub enum RelationshipStatus {
@@ -160,4 +164,46 @@ impl User {
             Ok(false)
         }
     }
+
+    /// Utility function for fetching multiple users from the perspective of one.
+    pub async fn fetch_multiple_users(&self, user_ids: Vec<String>) -> Result<Vec<User>> {
+        let mut users = vec![];
+        let mut cursor = get_collection("users")
+            .find(
+                doc! {
+                    "_id": {
+                        "$in": user_ids
+                    }
+                },
+                FindOptions::builder()
+                    .projection(
+                        doc! { "_id": 1, "username": 1, "avatar": 1, "badges": 1, "status": 1 },
+                    )
+                    .build(),
+            )
+            .await
+            .map_err(|_| Error::DatabaseError {
+                operation: "find",
+                with: "users",
+            })?;
+
+        while let Some(result) = cursor.next().await {
+            if let Ok(doc) = result {
+                let other: User = from_document(doc).map_err(|_| Error::DatabaseError {
+                    operation: "from_document",
+                    with: "user",
+                })?;
+
+                let permissions = PermissionCalculator::new(&self)
+                    .with_mutual_connection()
+                    .with_user(&other)
+                    .for_user_given()
+                    .await?;
+
+                users.push(other.from(&self).with(permissions));
+            }
+        }
+
+        Ok(users)
+    }
 }
diff --git a/src/main.rs b/src/main.rs
index 386e531..fdbe4df 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -15,9 +15,9 @@ extern crate ctrlc;
 
 pub mod database;
 pub mod notifications;
-pub mod version;
 pub mod routes;
 pub mod util;
+pub mod version;
 
 use async_std::task;
 use chrono::Duration;
@@ -40,7 +40,10 @@ async fn main() {
     dotenv::dotenv().ok();
     env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", "info"));
 
-    info!("Starting REVOLT server [version {}].", crate::version::VERSION);
+    info!(
+        "Starting REVOLT server [version {}].",
+        crate::version::VERSION
+    );
 
     util::variables::preflight_checks();
     database::connect().await;
diff --git a/src/notifications/events.rs b/src/notifications/events.rs
index 9366f16..327130b 100644
--- a/src/notifications/events.rs
+++ b/src/notifications/events.rs
@@ -35,12 +35,12 @@ pub enum RemoveUserField {
     ProfileContent,
     ProfileBackground,
     StatusText,
-    Avatar
+    Avatar,
 }
 
 #[derive(Serialize, Deserialize, Debug)]
 pub enum RemoveChannelField {
-    Icon
+    Icon,
 }
 
 #[derive(Serialize, Deserialize, Debug)]
@@ -67,7 +67,7 @@ pub enum ClientboundNotification {
         id: String,
         data: JsonValue,
         #[serde(skip_serializing_if = "Option::is_none")]
-        clear: Option<RemoveChannelField>
+        clear: Option<RemoveChannelField>,
     },
     ChannelGroupJoin {
         id: String,
@@ -93,7 +93,7 @@ pub enum ClientboundNotification {
         id: String,
         data: JsonValue,
         #[serde(skip_serializing_if = "Option::is_none")]
-        clear: Option<RemoveUserField>
+        clear: Option<RemoveUserField>,
     },
     UserRelationship {
         id: String,
@@ -110,7 +110,9 @@ impl ClientboundNotification {
     pub fn publish(self, topic: String) {
         async_std::task::spawn(async move {
             prehandle_hook(&self); // ! TODO: this should be moved to pubsub
-            hive_pubsub::backend::mongo::publish(get_hive(), &topic, self).await.ok();
+            hive_pubsub::backend::mongo::publish(get_hive(), &topic, self)
+                .await
+                .ok();
         });
     }
 }
diff --git a/src/notifications/payload.rs b/src/notifications/payload.rs
index bd36e7a..12e49b5 100644
--- a/src/notifications/payload.rs
+++ b/src/notifications/payload.rs
@@ -6,13 +6,9 @@ use crate::{
     util::result::{Error, Result},
 };
 use futures::StreamExt;
-use mongodb::{
-    bson::{doc, from_document},
-    options::FindOptions,
-};
+use mongodb::bson::{doc, from_document};
 
 pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> {
-    let mut users = vec![];
     let mut user_ids: HashSet<String> = HashSet::new();
 
     if let Some(relationships) = &user.relations {
@@ -68,41 +64,12 @@ pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> {
     }
 
     user_ids.remove(&user.id);
-    if user_ids.len() > 0 {
-        let mut cursor = get_collection("users")
-            .find(
-                doc! {
-                    "_id": {
-                        "$in": user_ids.into_iter().collect::<Vec<String>>()
-                    }
-                },
-                FindOptions::builder()
-                    .projection(doc! { "_id": 1, "username": 1, "avatar": 1, "badges": 1, "status": 1 })
-                    .build(),
-            )
-            .await
-            .map_err(|_| Error::DatabaseError {
-                operation: "find",
-                with: "users",
-            })?;
-
-        while let Some(result) = cursor.next().await {
-            if let Ok(doc) = result {
-                let other: User = from_document(doc).map_err(|_| Error::DatabaseError {
-                    operation: "from_document",
-                    with: "user",
-                })?;
-
-                let permissions = PermissionCalculator::new(&user)
-                    .with_mutual_connection()
-                    .with_user(&other)
-                    .for_user_given()
-                    .await?;
-
-                users.push(other.from(&user).with(permissions));
-            }
-        }
-    }
+    let mut users = if user_ids.len() > 0 {
+        user.fetch_multiple_users(user_ids.into_iter().collect::<Vec<String>>())
+            .await?
+    } else {
+        vec![]
+    };
 
     user.relationship = Some(RelationshipStatus::User);
     user.online = Some(true);
diff --git a/src/routes/channels/edit_channel.rs b/src/routes/channels/edit_channel.rs
index d5f114e..7c4f037 100644
--- a/src/routes/channels/edit_channel.rs
+++ b/src/routes/channels/edit_channel.rs
@@ -1,6 +1,6 @@
-use crate::{database::*, notifications::events::RemoveChannelField};
 use crate::notifications::events::ClientboundNotification;
 use crate::util::result::{Error, Result};
+use crate::{database::*, notifications::events::RemoveChannelField};
 
 use mongodb::bson::{doc, to_document};
 use rocket_contrib::json::Json;
@@ -17,7 +17,7 @@ pub struct Data {
     description: Option<String>,
     #[validate(length(min = 1, max = 128))]
     icon: Option<String>,
-    remove: Option<RemoveChannelField>
+    remove: Option<RemoveChannelField>,
 }
 
 #[patch("/<target>", data = "<data>")]
@@ -26,8 +26,12 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> {
     data.validate()
         .map_err(|error| Error::FailedValidation { error })?;
 
-    if data.name.is_none() && data.description.is_none() && data.icon.is_none() && data.remove.is_none() {
-        return Ok(())
+    if data.name.is_none()
+        && data.description.is_none()
+        && data.icon.is_none()
+        && data.remove.is_none()
+    {
+        return Ok(());
     }
 
     let target = target.fetch_channel().await?;
@@ -64,7 +68,8 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> {
             }
 
             if let Some(attachment_id) = &data.icon {
-                let attachment = File::find_and_use(&attachment_id, "icons", "object", &user.id).await?;
+                let attachment =
+                    File::find_and_use(&attachment_id, "icons", "object", &user.id).await?;
                 set.insert(
                     "icon",
                     to_document(&attachment).map_err(|_| Error::DatabaseError {
@@ -72,7 +77,7 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> {
                         with: "attachment",
                     })?,
                 );
-    
+
                 remove_icon = true;
             }
 
@@ -80,26 +85,25 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> {
             if set.len() > 0 {
                 operations.insert("$set", &set);
             }
-        
+
             if unset.len() > 0 {
                 operations.insert("$unset", unset);
             }
 
             if operations.len() > 0 {
                 get_collection("channels")
-                .update_one(
-                    doc! { "_id": &id },
-                    operations,
-                    None
-                )
-                .await
-                .map_err(|_| Error::DatabaseError { operation: "update_one", with: "channel" })?;
+                    .update_one(doc! { "_id": &id }, operations, None)
+                    .await
+                    .map_err(|_| Error::DatabaseError {
+                        operation: "update_one",
+                        with: "channel",
+                    })?;
             }
 
             ClientboundNotification::ChannelUpdate {
                 id: id.clone(),
                 data: json!(set),
-                clear: data.remove
+                clear: data.remove,
             }
             .publish(id.clone());
 
@@ -107,10 +111,7 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> {
                 Message::create(
                     "00000000000000000000000000".to_string(),
                     id.clone(),
-                    Content::SystemMessage(SystemMessage::ChannelRenamed {
-                        name,
-                        by: user.id,
-                    }),
+                    Content::SystemMessage(SystemMessage::ChannelRenamed { name, by: user.id }),
                 )
                 .publish(&target)
                 .await
diff --git a/src/routes/channels/fetch_members.rs b/src/routes/channels/fetch_members.rs
new file mode 100644
index 0000000..4f42e69
--- /dev/null
+++ b/src/routes/channels/fetch_members.rs
@@ -0,0 +1,23 @@
+use crate::database::*;
+use crate::util::result::{Error, Result};
+
+use rocket_contrib::json::JsonValue;
+
+#[get("/<target>/members")]
+pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
+    let target = target.fetch_channel().await?;
+
+    let perm = permissions::PermissionCalculator::new(&user)
+        .with_channel(&target)
+        .for_channel()
+        .await?;
+    if !perm.get_view() {
+        Err(Error::MissingPermission)?
+    }
+
+    if let Channel::Group { recipients, .. } = target {
+        Ok(json!(user.fetch_multiple_users(recipients).await?))
+    } else {
+        Err(Error::InvalidOperation)
+    }
+}
diff --git a/src/routes/channels/message_query.rs b/src/routes/channels/message_query.rs
index bed171c..903d811 100644
--- a/src/routes/channels/message_query.rs
+++ b/src/routes/channels/message_query.rs
@@ -14,7 +14,7 @@ use validator::Validate;
 #[derive(Serialize, Deserialize, FromFormValue)]
 pub enum Sort {
     Latest,
-    Oldest
+    Oldest,
 }
 
 #[derive(Validate, Serialize, Deserialize, FromForm)]
@@ -25,7 +25,7 @@ pub struct Options {
     before: Option<String>,
     #[validate(length(min = 26, max = 26))]
     after: Option<String>,
-    sort: Option<Sort>
+    sort: Option<Sort>,
 }
 
 #[get("/<target>/messages?<options..>")]
@@ -54,7 +54,11 @@ pub async fn req(user: User, target: Ref, options: Form<Options>) -> Result<Json
         query.insert("_id", doc! { "$gt": after });
     }
 
-    let sort = if let Sort::Latest = options.sort.as_ref().unwrap_or_else(|| &Sort::Latest) { -1 } else { 1 };
+    let sort = if let Sort::Latest = options.sort.as_ref().unwrap_or_else(|| &Sort::Latest) {
+        -1
+    } else {
+        1
+    };
     let mut cursor = get_collection("messages")
         .find(
             query,
diff --git a/src/routes/channels/mod.rs b/src/routes/channels/mod.rs
index 365474c..732f31d 100644
--- a/src/routes/channels/mod.rs
+++ b/src/routes/channels/mod.rs
@@ -3,6 +3,7 @@ use rocket::Route;
 mod delete_channel;
 mod edit_channel;
 mod fetch_channel;
+mod fetch_members;
 mod group_add_member;
 mod group_create;
 mod group_remove_member;
@@ -17,6 +18,7 @@ mod message_send;
 pub fn routes() -> Vec<Route> {
     routes![
         fetch_channel::req,
+        fetch_members::req,
         delete_channel::req,
         edit_channel::req,
         message_send::req,
diff --git a/src/routes/users/add_friend.rs b/src/routes/users/add_friend.rs
index ad0a494..b2c8697 100644
--- a/src/routes/users/add_friend.rs
+++ b/src/routes/users/add_friend.rs
@@ -77,14 +77,14 @@ pub async fn req(user: User, username: String) -> Result<JsonValue> {
                     ClientboundNotification::UserRelationship {
                         id: user.id.clone(),
                         user: target_user,
-                        status: RelationshipStatus::Friend
+                        status: RelationshipStatus::Friend,
                     }
                     .publish(user.id.clone());
 
                     ClientboundNotification::UserRelationship {
                         id: target_id.to_string(),
                         user,
-                        status: RelationshipStatus::Friend
+                        status: RelationshipStatus::Friend,
                     }
                     .publish(target_id.to_string());
 
@@ -134,18 +134,18 @@ pub async fn req(user: User, username: String) -> Result<JsonValue> {
                     let user = user
                         .from_override(&target_user, RelationshipStatus::Incoming)
                         .await?;
-                    
+
                     ClientboundNotification::UserRelationship {
                         id: user.id.clone(),
                         user: target_user,
-                        status: RelationshipStatus::Outgoing
+                        status: RelationshipStatus::Outgoing,
                     }
                     .publish(user.id.clone());
-                    
+
                     ClientboundNotification::UserRelationship {
                         id: target_id.to_string(),
                         user,
-                        status: RelationshipStatus::Incoming
+                        status: RelationshipStatus::Incoming,
                     }
                     .publish(target_id.to_string());
 
diff --git a/src/routes/users/block_user.rs b/src/routes/users/block_user.rs
index 6e06e0e..a786eb6 100644
--- a/src/routes/users/block_user.rs
+++ b/src/routes/users/block_user.rs
@@ -75,24 +75,24 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
             ) {
                 Ok(_) => {
                     let target = target
-                        .from_override(&user, RelationshipStatus::Friend)
+                        .from_override(&user, RelationshipStatus::Blocked)
                         .await?;
                     let user = user
-                        .from_override(&target, RelationshipStatus::Friend)
+                        .from_override(&target, RelationshipStatus::BlockedOther)
                         .await?;
                     let target_id = target.id.clone();
 
                     ClientboundNotification::UserRelationship {
                         id: user.id.clone(),
                         user: target,
-                        status: RelationshipStatus::Blocked
+                        status: RelationshipStatus::Blocked,
                     }
                     .publish(user.id.clone());
 
                     ClientboundNotification::UserRelationship {
                         id: target_id.clone(),
                         user,
-                        status: RelationshipStatus::BlockedOther
+                        status: RelationshipStatus::BlockedOther,
                     }
                     .publish(target_id);
 
@@ -145,14 +145,14 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
                     ClientboundNotification::UserRelationship {
                         id: user.id.clone(),
                         user: target,
-                        status: RelationshipStatus::Blocked
+                        status: RelationshipStatus::Blocked,
                     }
                     .publish(user.id.clone());
-                    
+
                     ClientboundNotification::UserRelationship {
                         id: target_id.clone(),
                         user,
-                        status: RelationshipStatus::BlockedOther
+                        status: RelationshipStatus::BlockedOther,
                     }
                     .publish(target_id);
 
diff --git a/src/routes/users/change_username.rs b/src/routes/users/change_username.rs
index 461818a..9d8d860 100644
--- a/src/routes/users/change_username.rs
+++ b/src/routes/users/change_username.rs
@@ -58,7 +58,7 @@ pub async fn req(
     ClientboundNotification::UserUpdate {
         id: user.id.clone(),
         data: json!(data.0),
-        clear: None
+        clear: None,
     }
     .publish(user.id.clone());
 
diff --git a/src/routes/users/edit_user.rs b/src/routes/users/edit_user.rs
index 2f1197e..9c89b2d 100644
--- a/src/routes/users/edit_user.rs
+++ b/src/routes/users/edit_user.rs
@@ -1,6 +1,6 @@
-use crate::{database::*, notifications::events::RemoveUserField};
 use crate::notifications::events::ClientboundNotification;
 use crate::util::result::{Error, Result};
+use crate::{database::*, notifications::events::RemoveUserField};
 
 use mongodb::bson::{doc, to_document};
 use rocket_contrib::json::Json;
@@ -25,7 +25,7 @@ pub struct Data {
     profile: Option<UserProfileData>,
     #[validate(length(min = 1, max = 128))]
     avatar: Option<String>,
-    remove: Option<RemoveUserField>
+    remove: Option<RemoveUserField>,
 }
 
 #[patch("/<_ignore_id>", data = "<data>")]
@@ -35,8 +35,12 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()>
     data.validate()
         .map_err(|error| Error::FailedValidation { error })?;
 
-    if data.status.is_none() && data.profile.is_none() && data.avatar.is_none() && data.remove.is_none() {
-        return Ok(())
+    if data.status.is_none()
+        && data.profile.is_none()
+        && data.avatar.is_none()
+        && data.remove.is_none()
+    {
+        return Ok(());
     }
 
     let mut unset = doc! {};
@@ -49,14 +53,14 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()>
         match remove {
             RemoveUserField::ProfileContent => {
                 unset.insert("profile.content", 1);
-            },
+            }
             RemoveUserField::ProfileBackground => {
                 unset.insert("profile.background", 1);
                 remove_background = true;
             }
             RemoveUserField::StatusText => {
                 unset.insert("status.text", 1);
-            },
+            }
             RemoveUserField::Avatar => {
                 unset.insert("avatar", 1);
                 remove_avatar = true;
@@ -80,7 +84,8 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()>
         }
 
         if let Some(attachment_id) = profile.background {
-            let attachment = File::find_and_use(&attachment_id, "backgrounds", "user", &user.id).await?;
+            let attachment =
+                File::find_and_use(&attachment_id, "backgrounds", "user", &user.id).await?;
             set.insert(
                 "profile.background",
                 to_document(&attachment).map_err(|_| Error::DatabaseError {
@@ -133,7 +138,7 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()>
         ClientboundNotification::UserUpdate {
             id: user.id.clone(),
             data: json!({ "status": status }),
-            clear: None
+            clear: None,
         }
         .publish(user.id.clone());
     }
@@ -142,7 +147,7 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()>
         ClientboundNotification::UserUpdate {
             id: user.id.clone(),
             data: json!({ "avatar": avatar }),
-            clear: None
+            clear: None,
         }
         .publish(user.id.clone());
     }
@@ -151,7 +156,7 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()>
         ClientboundNotification::UserUpdate {
             id: user.id.clone(),
             data: json!({}),
-            clear: Some(clear)
+            clear: Some(clear),
         }
         .publish(user.id.clone());
     }
diff --git a/src/routes/users/remove_friend.rs b/src/routes/users/remove_friend.rs
index 5426cf7..847e99c 100644
--- a/src/routes/users/remove_friend.rs
+++ b/src/routes/users/remove_friend.rs
@@ -56,14 +56,14 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
                     ClientboundNotification::UserRelationship {
                         id: user.id.clone(),
                         user: target,
-                        status: RelationshipStatus::None
+                        status: RelationshipStatus::None,
                     }
                     .publish(user.id.clone());
 
                     ClientboundNotification::UserRelationship {
                         id: target_id.clone(),
                         user,
-                        status: RelationshipStatus::None
+                        status: RelationshipStatus::None,
                     }
                     .publish(target_id);
 
diff --git a/src/routes/users/unblock_user.rs b/src/routes/users/unblock_user.rs
index 4e2e59c..6f94b18 100644
--- a/src/routes/users/unblock_user.rs
+++ b/src/routes/users/unblock_user.rs
@@ -85,14 +85,14 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
                         ClientboundNotification::UserRelationship {
                             id: user.id.clone(),
                             user: target,
-                            status: RelationshipStatus::None
+                            status: RelationshipStatus::None,
                         }
                         .publish(user.id.clone());
-                        
+
                         ClientboundNotification::UserRelationship {
                             id: target_id.clone(),
                             user: user,
-                            status: RelationshipStatus::None
+                            status: RelationshipStatus::None,
                         }
                         .publish(target_id);
 
-- 
GitLab