/** * welcum to the cache zone * Notes about data model here * Basically none of the ever gets written to disk so its mutability is * * Memory Model things * The cache should always own its own data and nothing else * On calls where the cache system needs to take data it should actually * make its own copy to not disturb the render side of things as it too requires * ownership of its own data. For this reason all parts of this are basically * going to be really selfish about its own data * */ use crate::api_types::Channel; use crate::net; use crate::command::Command; use crate::config::{ConfigFile, ServerConfig}; use std::collections::HashMap; use crate::api_types as api; type ChannelMeta = Channel; struct ChannelCache { pub meta: ChannelMeta, pub messages: Vec } struct ServerCache { // container for the relevant user/serer meta data // We use both parts in order to to maintain jwt's within the cache meta: ServerConfig, channels: HashMap // channel_id -> cache } pub struct Cache { // url -> Cache servers: HashMap, active_server: Option, active_text: Option, } impl ServerCache { pub fn from(meta: ServerConfig, channels: HashMap) -> Self { ServerCache { meta, channels } } } impl Cache { pub fn from(config: &ConfigFile) -> Cache { let mut servers = HashMap::new(); for servercfg in &config.servers { let i_server_cache = ServerCache::from(servercfg.clone(), HashMap::new()); let url = i_server_cache.meta.server.url.clone(); servers.insert(url, i_server_cache); } Cache { servers, active_server: None, active_text: None, } } pub async fn switch_channel(&mut self, id: u64) -> Command { if let None = self.active_server { return Command::Failure("No active server set!".into()) } let mut msg = String::new(); for (_url, cache) in &self.servers { if let Some(channel_cache) = cache.channels.get(&id) { let meta = channel_cache.meta.clone(); msg = format!("Switched to channel: [{}]: {}", meta.id, meta.name); self.active_text = Some(meta); break; } } if msg.len() == 0 { Command::Failure(format!("Channel not found in cache try /lc to refresh cache")) } else { Command::Text(msg) } } pub fn set_jwt(&mut self, url: &str, jwt: &str) -> bool { let mut set = false; for (surl, serv) in self.servers.iter_mut() { if url == surl { serv.meta.user.jwt = Some(jwt.into()); set = true; } }; return set; } fn _new_message(&mut self, msg: api::Message, url: String, chan_id: u64) { self.servers.get_mut(&url).unwrap() .channels.get_mut(&chan_id).unwrap() .messages.push(msg); } pub fn set_active(&mut self, url: &str) { for (surl, serv) in self.servers.iter() { if url == surl { self.active_server = Some(serv.meta.clone()); } } } pub async fn send_message(&self, msg: &str) -> Command { if let Some(channel) = &self.active_text { let config = self.active_server.as_ref().unwrap(); let chan = channel.id; let uid = config.user.id; let jwt = config.user.jwt.as_ref().unwrap(); let url = config.server.url.as_str(); return match net::send_text(url, uid, jwt, chan, msg).await { Ok(_) => Command::Message(msg.into()), Err(_) => Command::Failure("Couldn't send text message".into()) } } Command::Text("No active channel".into()) } pub fn list_hosts(&self) -> Command { let mut hosts = String::from("Hosts: "); for (host, _) in self.servers.iter() { hosts.push_str(format!("{}, ", host).as_str()); } Command::Text(hosts) } pub fn add_channels(&mut self, url: &str, channels: &[api::Channel]) { // if the url isn't there we just silently fail? let serv_ref = self.servers.get_mut(url).unwrap(); for chan in channels.iter() { let cache = ChannelCache { meta: chan.clone(), messages: Vec::new() }; serv_ref.channels.insert(chan.id, cache); } } pub fn session(&self) -> Option<(String, u64, String)>{ if let Some(serv) = &self.active_server { let id = serv.user.id; let url = serv.server.url.clone(); let jwt = serv.user.jwt.clone().unwrap_or("".to_string()); Some((url, id, jwt)) } else { None } } }