use std::fmt::Debug; use std::fs; use std::path::PathBuf; use dirs::home_dir; use std::io::{SeekFrom, Seek}; use reqwest::Client; use reqwest::header::{ACCEPT, CONTENT_TYPE}; use serde::de::DeserializeOwned; use serde::Serialize; use crate::error::{ManifoldError, ManifoldResult}; const CACHE_VERSION: &'static str = "1"; pub trait Pump { fn pump(&mut self) -> Option; fn len(&self) -> usize; } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct FuelTank { pub(crate) source_uri: String, cache_name: Option, pub(crate) api_key: Option, cache_mode: TankMode, } #[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub enum TankMode { Cache, NoCache, } impl FuelTank { pub async fn next_or_fill(&self) -> ManifoldResult where T: DeserializeOwned, U: Serialize, U: DeserializeOwned, U: Pump { debug!("Filling tank!"); let mut cache = { let mut path = PathBuf::new(); path.push(home_dir().ok_or(ManifoldError::from("No home dir"))?); path.push(".local"); path.push(format!("manifold-{}-cache-{}.json", self.cache_name.clone().unwrap(), CACHE_VERSION)); debug!("Cache path: {:?}", &path); fs::OpenOptions::new() .read(true) .write(true) .create(true) .open(path) .map_err(|e| { error!("Error creating cache file: {}", e); ManifoldError::from("CACHE ERROR") })? }; debug!("Cache: {:?}", &cache); let json: Result = serde_json::from_reader(&mut cache); debug!("Json: {:?}", &json); let mut fuel = match json { Ok(contents) => { match contents.len() { 0 => self.emit().await?, _ => contents, } } _ => self.emit().await?, }; debug!("Got tips: {:?}", &fuel); let single = fuel.pump().ok_or(ManifoldError::from("CACHE ERROR"))?; debug!("Got single tip: {:?}", &single); cache .set_len(0) .and_then(|_| cache.seek(SeekFrom::Start(0)))?; debug!("Writing cache"); serde_json::to_writer(cache, &fuel)?; return Ok(single) } pub async fn emit(&self) -> ManifoldResult where U: DeserializeOwned { debug!("Getting new tips"); let result: U; let mut client = Client::new().get(&self.source_uri) .header(CONTENT_TYPE, "application/json") .header(ACCEPT, "application/json"); if let Some(api_key) = &self.api_key { client = client.header("X-API-KEY", api_key); } result = client.send().await?.json().await?; debug!("Result: {:?}", &result); Ok(result) } }