diff --git a/src/data.rs b/src/data.rs index a29bd11..fda2a7a 100644 --- a/src/data.rs +++ b/src/data.rs @@ -18,19 +18,23 @@ use std::path::Path; use actix_web::web; use graphql_client::{reqwest::post_graphql, GraphQLQuery}; +use reqwest::header::USER_AGENT; use reqwest::Client; use serde::{Deserialize, Serialize}; use sled::{Db, Tree}; +use crate::proxy::StringUtils; use crate::SETTINGS; -const CACHE_VERSION: usize = 1; +const POST_CACHE_VERSION: usize = 1; +const GIST_CACHE_VERSION: usize = 1; #[derive(Clone)] pub struct Data { pub client: Client, cache: Db, pub posts: Tree, + pub gists: Tree, } #[derive(GraphQLQuery)] @@ -45,15 +49,51 @@ pub type PostResp = get_post::GetPostPost; pub type AppData = web::Data; +impl PostResp { + pub fn get_gist_id<'a>(&self, url: &'a str) -> &'a str { + url.split('/').last().unwrap() + } +} + +#[derive(Deserialize, Serialize)] +pub struct GistContent { + pub files: Vec, + pub html_url: String, +} + +#[derive(Deserialize, Serialize)] +pub struct GistFile { + pub file_name: String, + pub content: String, + pub language: String, + pub raw_url: String, +} + +impl GistFile { + pub fn get_html_content(&self) -> String { + let mut content = self.content.as_str(); + if self.content.starts_with('"') { + content = self.content.slice(1..); + } + + if content.ends_with('"') { + content = content.slice(..content.len() - 1); + } + content.replace("\\t", " ") + } +} + impl Data { pub fn new() -> AppData { let path = Path::new(SETTINGS.cache.as_ref().unwrap()).join("posts_cache"); let cache = sled::open(path).unwrap(); let posts = cache.open_tree("posts").unwrap(); + let gists = cache.open_tree("gists").unwrap(); let res = Self { client: Client::new(), cache, posts, + gists, }; res.migrate(); @@ -61,19 +101,31 @@ impl Data { } fn migrate(&self) { - const KEY: &str = "POST_CACHE_VERSION"; - let mut clean = true; - if let Ok(Some(v)) = self.posts.get(KEY) { - let version = bincode::deserialize::(&v[..]).unwrap(); - clean = !(version == CACHE_VERSION); - } + const POST_KEY: &str = "POST_CACHE_VERSION"; + const GIST_KEY: &str = "GIST_CACHE_VERSION"; + let trees = [ + (&self.posts, POST_KEY, POST_CACHE_VERSION), + (&self.gists, GIST_KEY, GIST_CACHE_VERSION), + ]; - if clean { - self.posts.clear().unwrap(); - self.posts.flush().unwrap(); - self.posts - .insert(KEY, bincode::serialize(&CACHE_VERSION).unwrap()) - .unwrap(); + for (tree, key, current_version) in trees { + if let Ok(Some(v)) = tree.get(key) { + let version = bincode::deserialize::(&v[..]).unwrap(); + let clean = !(version == current_version); + + if clean { + log::info!( + "Upgrading {} from version {} to version {}", + key, + version, + current_version + ); + tree.clear().unwrap(); + tree.flush().unwrap(); + tree.insert(key, bincode::serialize(¤t_version).unwrap()) + .unwrap(); + } + } } } @@ -95,4 +147,62 @@ impl Data { } } } + + pub async fn get_gist(&self, id: String) -> (String, GistContent) { + match self.gists.get(&id) { + Ok(Some(v)) => (id, bincode::deserialize(&v[..]).unwrap()), + _ => { + const URL: &str = "https://api.github.com/gists/"; + + let url = format!("{}{}", URL, id); + + let resp = self + .client + .get(&url) + .header(USER_AGENT, "libmedium") + .send() + .await + .unwrap() + .json::() + .await + .unwrap(); + let files = resp.get("files").unwrap(); + let v = files.as_object().unwrap(); + let mut files = Vec::with_capacity(v.len()); + v.iter().for_each(|(name, file_obj)| { + let file = GistFile { + file_name: name.to_string(), + content: file_obj + .get("content") + .unwrap() + .as_str() + .unwrap() + .to_owned(), + language: file_obj + .get("language") + .unwrap() + .as_str() + .unwrap() + .to_owned(), + raw_url: file_obj + .get("raw_url") + .unwrap() + .as_str() + .unwrap() + .to_owned(), + }; + files.push(file); + }); + let gist = GistContent { + files, + html_url: resp.get("html_url").unwrap().to_string(), + }; + + self.gists + .insert(&id, bincode::serialize(&gist).unwrap()) + .unwrap(); + (id, gist) + } + } + } }