Files
stoatchat/src/database/channel.rs
2020-08-10 18:53:24 +02:00

193 lines
5.7 KiB
Rust

use super::get_collection;
use lru::LruCache;
use mongodb::bson::{doc, from_bson, Bson};
use rocket::http::RawStr;
use rocket::request::FromParam;
use serde::{Deserialize, Serialize};
use std::sync::{Arc, Mutex};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct LastMessage {
// message id
id: String,
// author's id
user_id: String,
// truncated content with author's name prepended (for GDM / GUILD)
short_content: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Channel {
#[serde(rename = "_id")]
pub id: String,
#[serde(rename = "type")]
pub channel_type: u8,
// DM: whether the DM is active
pub active: Option<bool>,
// DM + GDM: last message in channel
pub last_message: Option<LastMessage>,
// DM + GDM: recipients for channel
pub recipients: Option<Vec<String>>,
// GDM: owner of group
pub owner: Option<String>,
// GUILD: channel parent
pub guild: Option<String>,
// GUILD + GDM: channel name
pub name: Option<String>,
// GUILD + GDM: channel description
pub description: Option<String>,
}
lazy_static! {
static ref CACHE: Arc<Mutex<LruCache<String, Channel>>> =
Arc::new(Mutex::new(LruCache::new(4_000_000)));
}
pub fn fetch_channel(id: &str) -> Result<Option<Channel>, String> {
{
if let Ok(mut cache) = CACHE.lock() {
let existing = cache.get(&id.to_string());
if let Some(channel) = existing {
return Ok(Some((*channel).clone()));
}
} else {
return Err("Failed to lock cache.".to_string());
}
}
let col = get_collection("channels");
if let Ok(result) = col.find_one(doc! { "_id": id }, None) {
if let Some(doc) = result {
if let Ok(channel) = from_bson(Bson::Document(doc)) as Result<Channel, _> {
let mut cache = CACHE.lock().unwrap();
cache.put(id.to_string(), channel.clone());
Ok(Some(channel))
} else {
Err("Failed to deserialize channel!".to_string())
}
} else {
Ok(None)
}
} else {
Err("Failed to fetch channel from database.".to_string())
}
}
pub fn fetch_channels(ids: &Vec<String>) -> Result<Option<Vec<Channel>>, String> {
let mut missing = vec![];
let mut channels = vec![];
{
if let Ok(mut cache) = CACHE.lock() {
for gid in ids {
let existing = cache.get(gid);
if let Some(channel) = existing {
channels.push((*channel).clone());
} else {
missing.push(gid);
}
}
} else {
return Err("Failed to lock cache.".to_string());
}
}
if missing.len() == 0 {
return Ok(Some(channels));
}
let col = get_collection("channels");
if let Ok(result) = col.find(doc! { "_id": { "$in": missing } }, None) {
for item in result {
let mut cache = CACHE.lock().unwrap();
if let Ok(doc) = item {
if let Ok(channel) = from_bson(Bson::Document(doc)) as Result<Channel, _> {
cache.put(channel.id.clone(), channel.clone());
channels.push(channel);
} else {
return Err("Failed to deserialize channel!".to_string());
}
} else {
return Err("Failed to fetch channel.".to_string());
}
}
Ok(Some(channels))
} else {
Err("Failed to fetch channel from database.".to_string())
}
}
impl<'r> FromParam<'r> for Channel {
type Error = &'r RawStr;
fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> {
if let Ok(result) = fetch_channel(param) {
if let Some(channel) = result {
Ok(channel)
} else {
Err(param)
}
} else {
Err(param)
}
}
}
use crate::notifications::events::Notification;
pub fn process_event(event: &Notification) {
match event {
Notification::group_user_join(ev) => {
let mut cache = CACHE.lock().unwrap();
let entry = cache.pop(&ev.id);
if entry.is_some() {
let mut channel = entry.unwrap();
channel.recipients.as_mut().unwrap().push(ev.user.clone());
cache.put(ev.id.clone(), channel);
}
}
Notification::group_user_leave(ev) => {
let mut cache = CACHE.lock().unwrap();
let entry = cache.pop(&ev.id);
if entry.is_some() {
let mut channel = entry.unwrap();
let recipients = channel.recipients.as_mut().unwrap();
if let Some(pos) = recipients.iter().position(|x| *x == ev.user) {
recipients.remove(pos);
}
cache.put(ev.id.clone(), channel);
}
}
Notification::guild_channel_create(ev) => {
let mut cache = CACHE.lock().unwrap();
cache.put(
ev.id.clone(),
Channel {
id: ev.channel.clone(),
channel_type: 2,
active: None,
last_message: None,
recipients: None,
owner: None,
guild: Some(ev.id.clone()),
name: Some(ev.name.clone()),
description: Some(ev.description.clone()),
},
);
}
Notification::guild_channel_delete(ev) => {
let mut cache = CACHE.lock().unwrap();
cache.pop(&ev.channel);
}
_ => {}
}
}