+ Adding list_channel handler

More agressive channel caching may be required here but it works as an MVP for now

* Switch to hashmap for container of channel caches instead of vector
This should help with cache accesses as we don't have to iterate anymore
Also it makes data fetches much cleaner/shorthanded

* net::list_channels has its own private structure for collecting channels vector
This is basically required and is in the scope of the func itsefl to not pollute any other namespaces
This commit is contained in:
shockrah 2021-04-07 20:38:06 -07:00
parent 8be5547d9e
commit 704f372c3e
3 changed files with 59 additions and 31 deletions

View File

@ -30,7 +30,7 @@ struct ServerCache {
// container for the relevant user/serer meta data // container for the relevant user/serer meta data
// We use both parts in order to to maintain jwt's within the cache // We use both parts in order to to maintain jwt's within the cache
meta: ServerConfig, meta: ServerConfig,
channels: Vec<ChannelCache> channels: HashMap<u64, ChannelCache> // channel_id -> cache
} }
pub struct Cache { pub struct Cache {
@ -41,7 +41,7 @@ pub struct Cache {
} }
impl ServerCache { impl ServerCache {
pub fn from(meta: ServerConfig, channels: Vec<ChannelCache>) -> Self { pub fn from(meta: ServerConfig, channels: HashMap<u64, ChannelCache>) -> Self {
ServerCache { meta, channels } ServerCache { meta, channels }
} }
} }
@ -50,7 +50,7 @@ impl Cache {
pub fn from(config: &ConfigFile) -> Cache { pub fn from(config: &ConfigFile) -> Cache {
let mut servers = HashMap::new(); let mut servers = HashMap::new();
for servercfg in &config.servers { for servercfg in &config.servers {
let i_server_cache = ServerCache::from(servercfg.clone(), Vec::new()); let i_server_cache = ServerCache::from(servercfg.clone(), HashMap::new());
let url = i_server_cache.meta.server.url.clone(); let url = i_server_cache.meta.server.url.clone();
servers.insert(url, i_server_cache); servers.insert(url, i_server_cache);
} }
@ -83,14 +83,18 @@ impl Cache {
let secret = &meta.meta.user.secret; let secret = &meta.meta.user.secret;
found = match net::login(url, id, secret).await { found = match net::login(url, id, secret).await {
Ok(jwt) => { Ok(jwt) => {
let twt = jwt.clone();
meta.meta.user.jwt = Some(jwt); meta.meta.user.jwt = Some(jwt);
self.active_server = Some(meta.meta.clone()); self.active_server = Some(meta.meta.clone());
// Setup websocket to server or something idk // TODO: Setup websocket to server or something idk
self.active_text = None; self.active_text = None;
true true
}, },
Err(e) => { // TODO: some kind of logging here Err(e) => {
err_msg = format!("{}", e); err_msg = match e.is_status() {
true => format!("{:?}", e.status().unwrap()),
false => format!("Issue connecting to server")
};
meta.meta.user.jwt = None; meta.meta.user.jwt = None;
false false
} }
@ -123,25 +127,30 @@ impl Cache {
Command::Text(hosts) Command::Text(hosts)
} }
pub fn list_channels(&self) -> Command { pub async fn list_channels(&mut self) -> Command {
use crate::api_types; if let None = self.active_server {
if let Some(serv) = &self.active_server { return Command::Failure("No active server".into())
// Find the server we're whose channels we want
let (_, cache) = self.servers.iter()
.find(|(url, _)| url == &&serv.server.url).unwrap();
let mut buffer = String::from("! Channels ");
for (i, chan) in cache.channels.iter().enumerate() {
// NOTE only list out text channels for now
// another command will be provided for voice channels later
if chan.meta.r#type == api_types::TEXT_CHANNEL {
buffer.push_str(&format!("[{}] {}, ", i, chan.meta.r#type));
} }
// we have to own our own reference here to avoid mutating after immutable borrows
let config = self.active_server.clone().unwrap();
let id = config.user.id;
let jwt = config.user.jwt.unwrap();
let url = config.server.url.as_str();
let mut buf = String::new();
match net::list_channels(url, id, jwt.as_str()).await {
Ok(mut channels) => {
for chan in channels.iter_mut() {
let id = chan.id;
let chan_cache = ChannelCache { meta: chan.clone(), messages: Vec::new() };
self.servers.get_mut(url).unwrap().channels.insert(id, chan_cache);
let chan = format!("{}: {},", id, chan.name);
buf.push_str(chan.as_str());
}
Command::Text(buf)
},
Err(e) => {
Command::Failure(format!("Couldn't fetch channels: {:?}", e.status()))
} }
Command::Text(buffer)
} else {
Command::Failure(format!("No active server"))
} }
} }
} }

View File

@ -226,7 +226,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
Command::Server(host) => app.cache.switch_server(&host).await, Command::Server(host) => app.cache.switch_server(&host).await,
Command::Message(msg) => app.cache.send_message(&msg).await, Command::Message(msg) => app.cache.send_message(&msg).await,
Command::ListHost => app.cache.list_hosts(), Command::ListHost => app.cache.list_hosts(),
Command::ListChannel => app.cache.list_channels(), Command::ListChannel => app.cache.list_channels().await,
_ => cmd _ => cmd
}); });
} }

View File

@ -1,19 +1,38 @@
use crate::api_types::Channel; use serde::Deserialize;
use crate::api_types::Jwt; use crate::api_types::{Jwt, Channel, TEXT_CHANNEL};
use reqwest::{Client, Url}; use reqwest::{Client, Url};
use reqwest::Result as HttpResult; use reqwest::Result as HttpResult;
// TODO: Url generation is kinda gross looking but i'm not 100%
// convinced it needs to be pretty if its just going to add overhead
pub async fn login(url: &str, id: u64, secret: &str) -> HttpResult<String> { pub async fn login(url: &str, id: u64, secret: &str) -> HttpResult<String> {
let client = Client::new(); let client = Client::new();
let mut url = Url::parse(&format!("{}/login", url)).unwrap(); let url = Url::parse_with_params(
url.query_pairs_mut().append_pair("id", &format!("{}", id)); &format!("{}/login", url),
url.query_pairs_mut().append_pair("secret", secret); &[("id", format!("{}", id).as_str()), ("secret", secret)]
).unwrap();
let response: Jwt = client.get(url).send().await?.json().await?; let response: Jwt = client.get(url).send().await?.json().await?;
Ok(response.jwt) Ok(response.jwt)
} }
fn list_channels(url: &str, id: u64, secret: &str) -> Vec<Channel> { pub async fn list_channels(url: &str, id: u64, jwt: &str) -> HttpResult<Vec<Channel>> {
todo!() let client = Client::new();
let url = Url::parse_with_params(
&format!("{}/channels/list", url),
&[
("id", format!("{}", id).as_str()),
("jwt", jwt),
("kind", format!("{}", TEXT_CHANNEL).as_str())
]
).unwrap();
#[derive(Deserialize)]
struct ChannelResponse {
pub channels: Vec<Channel>
}
let response: ChannelResponse = client.get(url).send().await?.json().await?;
Ok(response.channels)
} }