feat(core/database): implement user operations
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -2854,6 +2854,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"mongodb",
|
"mongodb",
|
||||||
"nanoid",
|
"nanoid",
|
||||||
|
"once_cell",
|
||||||
"revolt-permissions",
|
"revolt-permissions",
|
||||||
"revolt-result",
|
"revolt-result",
|
||||||
"revolt_optional_struct",
|
"revolt_optional_struct",
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ revolt-permissions = { version = "0.0.2", path = "../permissions" }
|
|||||||
# Utility
|
# Utility
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
nanoid = "0.4.0"
|
nanoid = "0.4.0"
|
||||||
|
once_cell = "1.17"
|
||||||
|
|
||||||
# Serialisation
|
# Serialisation
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ impl Bot {
|
|||||||
|
|
||||||
/// Delete this bot
|
/// Delete this bot
|
||||||
pub async fn delete(&self, db: &Database) -> Result<()> {
|
pub async fn delete(&self, db: &Database) -> Result<()> {
|
||||||
// db.fetch_user(&self.id).await?.mark_deleted(db).await?;
|
db.fetch_user(&self.id).await?.mark_deleted(db).await?;
|
||||||
db.delete_bot(&self.id).await
|
db.delete_bot(&self.id).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
use crate::File;
|
use crate::{Database, File};
|
||||||
|
|
||||||
|
use revolt_result::{Error, ErrorType, Result};
|
||||||
|
|
||||||
auto_derived_partial!(
|
auto_derived_partial!(
|
||||||
/// # User
|
/// # User
|
||||||
@@ -96,4 +98,99 @@ auto_derived!(
|
|||||||
/// Id of the owner of this bot
|
/// Id of the owner of this bot
|
||||||
pub owner: String,
|
pub owner: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Optional fields on user object
|
||||||
|
pub enum FieldsUser {
|
||||||
|
Avatar,
|
||||||
|
StatusText,
|
||||||
|
StatusPresence,
|
||||||
|
ProfileContent,
|
||||||
|
ProfileBackground,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
impl User {
|
||||||
|
/// Check whether a username is already in use by another user
|
||||||
|
async fn is_username_taken(db: &Database, username: &str) -> Result<bool> {
|
||||||
|
match db.fetch_user_by_username(username).await {
|
||||||
|
Ok(_) => Ok(true),
|
||||||
|
Err(Error {
|
||||||
|
error_type: ErrorType::NotFound,
|
||||||
|
..
|
||||||
|
}) => Ok(false),
|
||||||
|
Err(error) => Err(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update user data
|
||||||
|
pub async fn update<'a>(
|
||||||
|
&mut self,
|
||||||
|
db: &Database,
|
||||||
|
partial: PartialUser,
|
||||||
|
remove: Vec<FieldsUser>,
|
||||||
|
) -> Result<()> {
|
||||||
|
for field in &remove {
|
||||||
|
self.remove_field(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.apply_options(partial.clone());
|
||||||
|
db.update_user(&self.id, &partial, remove.clone()).await?;
|
||||||
|
|
||||||
|
/* // TODO: EventV1::UserUpdate {
|
||||||
|
id: self.id.clone(),
|
||||||
|
data: partial,
|
||||||
|
clear: remove,
|
||||||
|
}
|
||||||
|
.p_user(self.id.clone(), db)
|
||||||
|
.await; */
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a field from User object
|
||||||
|
pub fn remove_field(&mut self, field: &FieldsUser) {
|
||||||
|
match field {
|
||||||
|
FieldsUser::Avatar => self.avatar = None,
|
||||||
|
FieldsUser::StatusText => {
|
||||||
|
if let Some(x) = self.status.as_mut() {
|
||||||
|
x.text = String::new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FieldsUser::StatusPresence => {
|
||||||
|
if let Some(x) = self.status.as_mut() {
|
||||||
|
x.presence = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FieldsUser::ProfileContent => {
|
||||||
|
if let Some(x) = self.profile.as_mut() {
|
||||||
|
x.content = String::new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FieldsUser::ProfileBackground => {
|
||||||
|
if let Some(x) = self.profile.as_mut() {
|
||||||
|
x.background = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark as deleted
|
||||||
|
pub async fn mark_deleted(&mut self, db: &Database) -> Result<()> {
|
||||||
|
self.update(
|
||||||
|
db,
|
||||||
|
PartialUser {
|
||||||
|
username: Some(format!("Deleted User {}", self.id)),
|
||||||
|
flags: Some(2),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
vec![
|
||||||
|
FieldsUser::Avatar,
|
||||||
|
FieldsUser::StatusText,
|
||||||
|
FieldsUser::StatusPresence,
|
||||||
|
FieldsUser::ProfileContent,
|
||||||
|
FieldsUser::ProfileBackground,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,57 @@
|
|||||||
use revolt_result::Result;
|
use revolt_result::Result;
|
||||||
|
|
||||||
use crate::User;
|
use crate::{FieldsUser, PartialUser, RelationshipStatus, User};
|
||||||
|
|
||||||
mod mongodb;
|
mod mongodb;
|
||||||
mod reference;
|
mod reference;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait AbstractUsers: Sync + Send {
|
pub trait AbstractUsers: Sync + Send {
|
||||||
|
/// Insert a new user into the database
|
||||||
|
async fn insert_user(&self, user: &User) -> Result<()>;
|
||||||
|
|
||||||
/// Fetch a user from the database
|
/// Fetch a user from the database
|
||||||
async fn fetch_user(&self, id: &str) -> Result<User>;
|
async fn fetch_user(&self, id: &str) -> Result<User>;
|
||||||
|
|
||||||
|
/// Fetch a user from the database by their username
|
||||||
|
async fn fetch_user_by_username(&self, username: &str) -> Result<User>;
|
||||||
|
|
||||||
|
/// Fetch a user from the database by their session token
|
||||||
|
async fn fetch_user_by_token(&self, token: &str) -> Result<User>;
|
||||||
|
|
||||||
|
/// Fetch multiple users by their ids
|
||||||
|
async fn fetch_users<'a>(&self, ids: &'a [String]) -> Result<Vec<User>>;
|
||||||
|
|
||||||
|
/// Fetch ids of users that both users are friends with
|
||||||
|
async fn fetch_mutual_user_ids(&self, user_a: &str, user_b: &str) -> Result<Vec<String>>;
|
||||||
|
|
||||||
|
/// Fetch ids of channels that both users are in
|
||||||
|
async fn fetch_mutual_channel_ids(&self, user_a: &str, user_b: &str) -> Result<Vec<String>>;
|
||||||
|
|
||||||
|
/// Fetch ids of servers that both users share
|
||||||
|
async fn fetch_mutual_server_ids(&self, user_a: &str, user_b: &str) -> Result<Vec<String>>;
|
||||||
|
|
||||||
|
/// Update a user by their id given some data
|
||||||
|
async fn update_user(
|
||||||
|
&self,
|
||||||
|
id: &str,
|
||||||
|
user: &PartialUser,
|
||||||
|
remove: Vec<FieldsUser>,
|
||||||
|
) -> Result<()>;
|
||||||
|
|
||||||
|
/// Set relationship with another user
|
||||||
|
///
|
||||||
|
/// This should use pull_relationship if relationship is None.
|
||||||
|
async fn set_relationship(
|
||||||
|
&self,
|
||||||
|
user_id: &str,
|
||||||
|
target_id: &str,
|
||||||
|
relationship: &RelationshipStatus,
|
||||||
|
) -> Result<()>;
|
||||||
|
|
||||||
|
/// Remove relationship with another user
|
||||||
|
async fn pull_relationship(&self, user_id: &str, target_id: &str) -> Result<()>;
|
||||||
|
|
||||||
|
/// Delete a user by their id
|
||||||
|
async fn delete_user(&self, id: &str) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,300 @@
|
|||||||
|
use ::mongodb::options::{Collation, CollationStrength, FindOneOptions, FindOptions};
|
||||||
|
use authifier::models::Session;
|
||||||
|
use futures::StreamExt;
|
||||||
use revolt_result::Result;
|
use revolt_result::Result;
|
||||||
|
|
||||||
|
use crate::DocumentId;
|
||||||
|
use crate::IntoDocumentPath;
|
||||||
use crate::MongoDb;
|
use crate::MongoDb;
|
||||||
use crate::User;
|
use crate::{FieldsUser, PartialUser, RelationshipStatus, User};
|
||||||
|
|
||||||
use super::AbstractUsers;
|
use super::AbstractUsers;
|
||||||
|
|
||||||
static COL: &str = "bots";
|
static COL: &str = "users";
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl AbstractUsers for MongoDb {
|
impl AbstractUsers for MongoDb {
|
||||||
|
/// Insert a new user into the database
|
||||||
|
async fn insert_user(&self, user: &User) -> Result<()> {
|
||||||
|
query!(self, insert_one, COL, &user).map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
/// Fetch a user from the database
|
/// Fetch a user from the database
|
||||||
async fn fetch_user(&self, id: &str) -> Result<User> {
|
async fn fetch_user(&self, id: &str) -> Result<User> {
|
||||||
query!(self, find_one_by_id, COL, id)?.ok_or_else(|| create_error!(NotFound))
|
query!(self, find_one_by_id, COL, id)?.ok_or_else(|| create_error!(NotFound))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fetch a user from the database by their username
|
||||||
|
async fn fetch_user_by_username(&self, username: &str) -> Result<User> {
|
||||||
|
query!(
|
||||||
|
self,
|
||||||
|
find_one_with_options,
|
||||||
|
COL,
|
||||||
|
doc! {
|
||||||
|
"username": username
|
||||||
|
},
|
||||||
|
FindOneOptions::builder()
|
||||||
|
.collation(
|
||||||
|
Collation::builder()
|
||||||
|
.locale("en")
|
||||||
|
.strength(CollationStrength::Secondary)
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
)?
|
||||||
|
.ok_or_else(|| create_error!(NotFound))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch a user from the database by their session token
|
||||||
|
async fn fetch_user_by_token(&self, token: &str) -> Result<User> {
|
||||||
|
let session = self
|
||||||
|
.col::<Session>("sessions")
|
||||||
|
.find_one(
|
||||||
|
doc! {
|
||||||
|
"token": token
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| create_database_error!("find_one", "sessions"))?
|
||||||
|
.ok_or_else(|| create_error!(InvalidSession))?;
|
||||||
|
|
||||||
|
self.fetch_user(&session.id).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch multiple users by their ids
|
||||||
|
async fn fetch_users<'a>(&self, ids: &'a [String]) -> Result<Vec<User>> {
|
||||||
|
Ok(self
|
||||||
|
.col::<User>(COL)
|
||||||
|
.find(
|
||||||
|
doc! {
|
||||||
|
"_id": {
|
||||||
|
"$in": ids
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| create_database_error!("find", "user"))?
|
||||||
|
.filter_map(|s| async {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
Some(s.unwrap())
|
||||||
|
} else {
|
||||||
|
s.ok()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
.await)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch ids of users that both users are friends with
|
||||||
|
async fn fetch_mutual_user_ids(&self, user_a: &str, user_b: &str) -> Result<Vec<String>> {
|
||||||
|
Ok(self
|
||||||
|
.col::<DocumentId>(COL)
|
||||||
|
.find(
|
||||||
|
doc! {
|
||||||
|
"$and": [
|
||||||
|
{ "relations": { "$elemMatch": { "_id": &user_a, "status": "Friend" } } },
|
||||||
|
{ "relations": { "$elemMatch": { "_id": &user_b, "status": "Friend" } } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
FindOptions::builder().projection(doc! { "_id": 1 }).build(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| create_database_error!("find", "users"))?
|
||||||
|
.filter_map(|s| async { s.ok() })
|
||||||
|
.map(|user| user.id)
|
||||||
|
.collect()
|
||||||
|
.await)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch ids of channels that both users are in
|
||||||
|
async fn fetch_mutual_channel_ids(&self, user_a: &str, user_b: &str) -> Result<Vec<String>> {
|
||||||
|
Ok(self
|
||||||
|
.col::<DocumentId>("channels")
|
||||||
|
.find(
|
||||||
|
doc! {
|
||||||
|
"channel_type": {
|
||||||
|
"$in": ["Group", "DirectMessage"]
|
||||||
|
},
|
||||||
|
"recipients": {
|
||||||
|
"$all": [ user_a, user_b ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
FindOptions::builder().projection(doc! { "_id": 1 }).build(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| create_database_error!("find", "channels"))?
|
||||||
|
.filter_map(|s| async { s.ok() })
|
||||||
|
.map(|user| user.id)
|
||||||
|
.collect()
|
||||||
|
.await)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch ids of servers that both users share
|
||||||
|
async fn fetch_mutual_server_ids(&self, user_a: &str, user_b: &str) -> Result<Vec<String>> {
|
||||||
|
Ok(self
|
||||||
|
.col::<DocumentId>("server_members")
|
||||||
|
.aggregate(
|
||||||
|
vec![
|
||||||
|
doc! {
|
||||||
|
"$match": {
|
||||||
|
"_id.user": user_a
|
||||||
|
}
|
||||||
|
},
|
||||||
|
doc! {
|
||||||
|
"$lookup": {
|
||||||
|
"from": "server_members",
|
||||||
|
"as": "members",
|
||||||
|
"let": {
|
||||||
|
"server": "$_id.server"
|
||||||
|
},
|
||||||
|
"pipeline": [
|
||||||
|
{
|
||||||
|
"$match": {
|
||||||
|
"$expr": {
|
||||||
|
"$and": [
|
||||||
|
{ "$eq": [ "$_id.user", user_b ] },
|
||||||
|
{ "$eq": [ "$_id.server", "$$server" ] }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
doc! {
|
||||||
|
"$match": {
|
||||||
|
"members": {
|
||||||
|
"$size": 1_i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
doc! {
|
||||||
|
"$project": {
|
||||||
|
"_id": "$_id.server"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| create_database_error!("aggregate", "server_members"))?
|
||||||
|
.filter_map(|s| async { s.ok() })
|
||||||
|
.filter_map(|doc| async move { doc.get_str("_id").map(|id| id.to_string()).ok() })
|
||||||
|
.collect()
|
||||||
|
.await)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update a user by their id given some data
|
||||||
|
async fn update_user(
|
||||||
|
&self,
|
||||||
|
id: &str,
|
||||||
|
partial: &PartialUser,
|
||||||
|
remove: Vec<FieldsUser>,
|
||||||
|
) -> Result<()> {
|
||||||
|
query!(
|
||||||
|
self,
|
||||||
|
update_one_by_id,
|
||||||
|
COL,
|
||||||
|
id,
|
||||||
|
partial,
|
||||||
|
remove.iter().map(|x| x as &dyn IntoDocumentPath).collect(),
|
||||||
|
None
|
||||||
|
)
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set relationship with another user
|
||||||
|
///
|
||||||
|
/// This should use pull_relationship if relationship is None.
|
||||||
|
async fn set_relationship(
|
||||||
|
&self,
|
||||||
|
user_id: &str,
|
||||||
|
target_id: &str,
|
||||||
|
relationship: &RelationshipStatus,
|
||||||
|
) -> Result<()> {
|
||||||
|
if let RelationshipStatus::None = relationship {
|
||||||
|
return self.pull_relationship(user_id, target_id).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.col::<User>(COL)
|
||||||
|
.update_one(
|
||||||
|
doc! {
|
||||||
|
"_id": user_id
|
||||||
|
},
|
||||||
|
vec![doc! {
|
||||||
|
"$set": {
|
||||||
|
"relations": {
|
||||||
|
"$concatArrays": [
|
||||||
|
{
|
||||||
|
"$ifNull": [
|
||||||
|
{
|
||||||
|
"$filter": {
|
||||||
|
"input": "$relations",
|
||||||
|
"cond": {
|
||||||
|
"$ne": [
|
||||||
|
"$$this._id",
|
||||||
|
target_id
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"_id": target_id,
|
||||||
|
"status": format!("{relationship:?}")
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|_| create_database_error!("update_one", "user"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove relationship with another user
|
||||||
|
async fn pull_relationship(&self, user_id: &str, target_id: &str) -> Result<()> {
|
||||||
|
self.col::<User>(COL)
|
||||||
|
.update_one(
|
||||||
|
doc! {
|
||||||
|
"_id": user_id
|
||||||
|
},
|
||||||
|
doc! {
|
||||||
|
"$pull": {
|
||||||
|
"relations": {
|
||||||
|
"_id": target_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|_| create_database_error!("update_one", "user"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete a user by their id
|
||||||
|
async fn delete_user(&self, id: &str) -> Result<()> {
|
||||||
|
query!(self, delete_one_by_id, COL, id).map(|_| ())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoDocumentPath for FieldsUser {
|
||||||
|
fn as_path(&self) -> Option<&'static str> {
|
||||||
|
Some(match self {
|
||||||
|
FieldsUser::Avatar => "avatar",
|
||||||
|
FieldsUser::ProfileBackground => "profile.background",
|
||||||
|
FieldsUser::ProfileContent => "profile.content",
|
||||||
|
FieldsUser::StatusPresence => "status.presence",
|
||||||
|
FieldsUser::StatusText => "status.text",
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,23 @@
|
|||||||
use revolt_result::Result;
|
use revolt_result::Result;
|
||||||
|
|
||||||
use crate::ReferenceDb;
|
use crate::ReferenceDb;
|
||||||
use crate::User;
|
use crate::{FieldsUser, PartialUser, RelationshipStatus, User};
|
||||||
|
|
||||||
use super::AbstractUsers;
|
use super::AbstractUsers;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl AbstractUsers for ReferenceDb {
|
impl AbstractUsers for ReferenceDb {
|
||||||
|
/// Insert a new user into the database
|
||||||
|
async fn insert_user(&self, user: &User) -> Result<()> {
|
||||||
|
let mut users = self.users.lock().await;
|
||||||
|
if users.contains_key(&user.id) {
|
||||||
|
Err(create_database_error!("insert", "user"))
|
||||||
|
} else {
|
||||||
|
users.insert(user.id.to_string(), user.clone());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Fetch a user from the database
|
/// Fetch a user from the database
|
||||||
async fn fetch_user(&self, id: &str) -> Result<User> {
|
async fn fetch_user(&self, id: &str) -> Result<User> {
|
||||||
let users = self.users.lock().await;
|
let users = self.users.lock().await;
|
||||||
@@ -15,4 +26,96 @@ impl AbstractUsers for ReferenceDb {
|
|||||||
.cloned()
|
.cloned()
|
||||||
.ok_or_else(|| create_error!(NotFound))
|
.ok_or_else(|| create_error!(NotFound))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fetch a user from the database by their username
|
||||||
|
async fn fetch_user_by_username(&self, username: &str) -> Result<User> {
|
||||||
|
let users = self.users.lock().await;
|
||||||
|
let lowercase = username.to_lowercase();
|
||||||
|
users
|
||||||
|
.values()
|
||||||
|
.find(|user| user.username.to_lowercase() == lowercase)
|
||||||
|
.cloned()
|
||||||
|
.ok_or_else(|| create_error!(NotFound))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch a user from the database by their session token
|
||||||
|
async fn fetch_user_by_token(&self, _token: &str) -> Result<User> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch multiple users by their ids
|
||||||
|
async fn fetch_users<'a>(&self, ids: &'a [String]) -> Result<Vec<User>> {
|
||||||
|
let users = self.users.lock().await;
|
||||||
|
ids.iter()
|
||||||
|
.map(|id| {
|
||||||
|
users
|
||||||
|
.get(id)
|
||||||
|
.cloned()
|
||||||
|
.ok_or_else(|| create_error!(NotFound))
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch ids of users that both users are friends with
|
||||||
|
async fn fetch_mutual_user_ids(&self, _user_a: &str, _user_b: &str) -> Result<Vec<String>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch ids of channels that both users are in
|
||||||
|
async fn fetch_mutual_channel_ids(&self, _user_a: &str, _user_b: &str) -> Result<Vec<String>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch ids of servers that both users share
|
||||||
|
async fn fetch_mutual_server_ids(&self, _user_a: &str, _user_b: &str) -> Result<Vec<String>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update a user by their id given some data
|
||||||
|
async fn update_user(
|
||||||
|
&self,
|
||||||
|
id: &str,
|
||||||
|
partial: &PartialUser,
|
||||||
|
remove: Vec<FieldsUser>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut users = self.users.lock().await;
|
||||||
|
if let Some(user) = users.get_mut(id) {
|
||||||
|
for field in remove {
|
||||||
|
#[allow(clippy::disallowed_methods)]
|
||||||
|
user.remove_field(&field);
|
||||||
|
}
|
||||||
|
|
||||||
|
user.apply_options(partial.clone());
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(create_error!(NotFound))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set relationship with another user
|
||||||
|
///
|
||||||
|
/// This should use pull_relationship if relationship is None.
|
||||||
|
async fn set_relationship(
|
||||||
|
&self,
|
||||||
|
_user_id: &str,
|
||||||
|
_target_id: &str,
|
||||||
|
_relationship: &RelationshipStatus,
|
||||||
|
) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove relationship with another user
|
||||||
|
async fn pull_relationship(&self, _user_id: &str, _target_id: &str) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete a user by their id
|
||||||
|
async fn delete_user(&self, id: &str) -> Result<()> {
|
||||||
|
let mut users = self.users.lock().await;
|
||||||
|
if users.remove(id).is_some() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(create_error!(NotFound))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user