use bson::{to_document, Bson, Document};
use futures::StreamExt;
use revolt_result::Result;
use crate::{FieldsRole, FieldsServer, PartialRole, PartialServer, Role, Server};
use crate::{IntoDocumentPath, MongoDb};
use super::AbstractServers;
static COL: &str = "servers";
#[async_trait]
impl AbstractServers for MongoDb {
async fn insert_server(&self, server: &Server) -> Result<()> {
query!(self, insert_one, COL, &server).map(|_| ())
}
async fn fetch_server(&self, id: &str) -> Result<Server> {
query!(self, find_one_by_id, COL, id)?.ok_or_else(|| create_error!(NotFound))
}
async fn fetch_servers<'a>(&self, ids: &'a [String]) -> Result<Vec<Server>> {
Ok(self
.col::<Server>(COL)
.find(
doc! {
"_id": {
"$in": ids
}
},
None,
)
.await
.map_err(|_| create_database_error!("find", "servers"))?
.filter_map(|s| async {
if cfg!(debug_assertions) {
Some(s.unwrap())
} else {
s.ok()
}
})
.collect()
.await)
}
async fn update_server(
&self,
id: &str,
partial: &PartialServer,
remove: Vec<FieldsServer>,
) -> Result<()> {
query!(
self,
update_one_by_id,
COL,
id,
partial,
remove.iter().map(|x| x as &dyn IntoDocumentPath).collect(),
None
)
.map(|_| ())
}
async fn delete_server(&self, id: &str) -> Result<()> {
self.delete_associated_server_objects(id).await?;
query!(self, delete_one_by_id, COL, id).map(|_| ())
}
async fn insert_role(&self, server_id: &str, role_id: &str, role: &Role) -> Result<()> {
self.col::<Document>(COL)
.update_one(
doc! {
"_id": server_id
},
doc! {
"$set": {
"roles.".to_owned() + role_id: to_document(role)
.map_err(|_| create_database_error!("to_document", "role"))?
}
},
None,
)
.await
.map(|_| ())
.map_err(|_| create_database_error!("update_one", "server"))
}
async fn update_role(
&self,
server_id: &str,
role_id: &str,
partial: &PartialRole,
remove: Vec<FieldsRole>,
) -> Result<()> {
query!(
self,
update_one_by_id,
COL,
server_id,
partial,
remove.iter().map(|x| x as &dyn IntoDocumentPath).collect(),
"roles.".to_owned() + role_id + "."
)
.map(|_| ())
}
async fn delete_role(&self, server_id: &str, role_id: &str) -> Result<()> {
self.col::<Document>("server_members")
.update_many(
doc! {
"_id.server": server_id
},
doc! {
"$pull": {
"roles": &role_id
}
},
None,
)
.await
.map_err(|_| create_database_error!("update_many", "server_members"))?;
self.col::<Document>("channels")
.update_one(
doc! {
"server": server_id
},
doc! {
"$unset": {
"role_permissions.".to_owned() + role_id: 1_i32
}
},
None,
)
.await
.map_err(|_| create_database_error!("update_one", "channels"))?;
self.col::<Document>("servers")
.update_one(
doc! {
"_id": server_id
},
doc! {
"$unset": {
"roles.".to_owned() + role_id: 1_i32
}
},
None,
)
.await
.map(|_| ())
.map_err(|_| create_database_error!("update_one", "servers"))
}
}
impl IntoDocumentPath for FieldsServer {
fn as_path(&self) -> Option<&'static str> {
Some(match self {
FieldsServer::Banner => "banner",
FieldsServer::Categories => "categories",
FieldsServer::Description => "description",
FieldsServer::Icon => "icon",
FieldsServer::SystemMessages => "system_messages",
})
}
}
impl IntoDocumentPath for FieldsRole {
fn as_path(&self) -> Option<&'static str> {
Some(match self {
FieldsRole::Colour => "colour",
})
}
}
impl MongoDb {
pub async fn delete_associated_server_objects(&self, server_id: &str) -> Result<()> {
let channels: Vec<String> = self
.col::<Document>("channels")
.find(
doc! {
"server": server_id
},
None,
)
.await
.map_err(|_| create_database_error!("find", "channels"))?
.filter_map(|s| async {
s.map(|d| d.get_str("_id").map(|s| s.to_string()).ok())
.ok()
.flatten()
})
.collect()
.await;
self.delete_bulk_messages(doc! {
"channel": {
"$in": &channels
}
})
.await?;
self.col::<Document>("emojis")
.delete_many(
doc! {
"parent.id": &server_id
},
None,
)
.await
.map_err(|_| create_database_error!("delete_many", "emojis"))?;
self.col::<Document>("channels")
.delete_many(
doc! {
"server": &server_id
},
None,
)
.await
.map_err(|_| create_database_error!("delete_many", "channels"))?;
self.delete_associated_channel_objects(Bson::Document(doc! { "$in": &channels }))
.await?;
for with in &["server_members", "server_bans"] {
self.col::<Document>(with)
.delete_many(
doc! {
"_id.server": &server_id
},
None,
)
.await
.map_err(|_| create_database_error!("delete_many", with))?;
}
self.delete_many_attachments(doc! {
"object_id": &server_id
})
.await?;
Ok(())
}
}