use async_trait::async_trait; use std::collections::HashMap; use std::sync::Arc; use async_std::sync::RwLock; use futures_util::FutureExt; use serde_json::json; use crate::database::{Storage, Result, StorageError, ErrorKind, MicropubChannel}; use crate::indieauth::User; #[derive(Clone)] pub struct MemoryStorage { pub mapping: Arc>>, pub channels: Arc>>> } #[async_trait] impl Storage for MemoryStorage { async fn read_feed_with_limit<'a>(&self, url: &'a str, after: &'a Option, limit: usize, user: &'a Option) -> Result> { todo!() } async fn update_post<'a>(&self, url: &'a str, update: serde_json::Value) -> Result<()> { todo!() } async fn delete_post<'a>(&self, url: &'a str) -> Result<()> { self.mapping.write().await.remove(url); Ok(()) } async fn post_exists(&self, url: &str) -> Result { return Ok(self.mapping.read().await.contains_key(url)) } async fn get_post(&self, url: &str) ->Result> { let mapping = self.mapping.read().await; match mapping.get(url) { Some(val) => { if let Some(new_url) = val["see_other"].as_str() { match mapping.get(new_url) { Some(val) => Ok(Some(val.clone())), None => { drop(mapping); self.mapping.write().await.remove(url); Ok(None) } } } else { Ok(Some(val.clone())) } }, _ => Ok(None) } } async fn get_channels(&self, user: &User) -> Result> { match self.channels.read().await.get(&user.me.to_string()) { Some(channels) => Ok(futures_util::future::join_all(channels.iter() .map(|channel| self.get_post(channel) .map(|result| result.unwrap()) .map(|post: Option| { if let Some(post) = post { Some(MicropubChannel { uid: post["properties"]["uid"][0].as_str().unwrap().to_string(), name: post["properties"]["name"][0].as_str().unwrap().to_string() }) } else { None } }) ).collect::>()).await.into_iter().filter_map(|chan| chan).collect::>()), None => Ok(vec![]) } } async fn put_post<'a>(&self, post: &'a serde_json::Value) -> Result<()> { let mapping = &mut self.mapping.write().await; let key: &str; match post["properties"]["uid"][0].as_str() { Some(uid) => key = uid, None => return Err(StorageError::new(ErrorKind::Other, "post doesn't have a UID")) } mapping.insert(key.to_string(), post.clone()); if post["properties"]["url"].is_array() { for url in post["properties"]["url"].as_array().unwrap().iter().map(|i| i.as_str().unwrap().to_string()) { if &url != key { mapping.insert(url, json!({"see_other": key})); } } } if post["type"].as_array().unwrap().iter().any(|i| i == "h-feed") { // This is a feed. Add it to the channels array if it's not already there. println!("{:#}", post); self.channels.write().await.entry(post["properties"]["author"][0].as_str().unwrap().to_string()).or_insert(vec![]).push(key.to_string()) } Ok(()) } } impl MemoryStorage { pub fn new() -> Self { Self { mapping: Arc::new(RwLock::new(HashMap::new())), channels: Arc::new(RwLock::new(HashMap::new())) } } }