2023-09-20 22:22:30 +00:00
|
|
|
use std::env::temp_dir;
|
2023-08-30 15:53:24 +00:00
|
|
|
use std::fmt::Debug;
|
2023-08-24 21:39:38 +00:00
|
|
|
use std::fs;
|
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
use std::io::{SeekFrom, Seek};
|
|
|
|
|
use reqwest::Client;
|
2023-08-30 17:44:39 +00:00
|
|
|
use reqwest::header::{ACCEPT, CONTENT_TYPE};
|
2023-08-30 15:53:24 +00:00
|
|
|
use serde::de::DeserializeOwned;
|
|
|
|
|
use serde::Serialize;
|
2023-08-24 21:39:38 +00:00
|
|
|
|
|
|
|
|
use crate::error::{ManifoldError, ManifoldResult};
|
|
|
|
|
|
|
|
|
|
const CACHE_VERSION: &'static str = "1";
|
|
|
|
|
|
|
|
|
|
pub trait Pump<V> {
|
2023-08-30 15:53:24 +00:00
|
|
|
fn pump(&mut self) -> Option<V>;
|
|
|
|
|
fn len(&self) -> usize;
|
2023-08-24 21:39:38 +00:00
|
|
|
}
|
|
|
|
|
|
2023-08-30 15:53:24 +00:00
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
2023-08-24 21:39:38 +00:00
|
|
|
pub struct FuelTank {
|
2023-08-30 15:53:24 +00:00
|
|
|
pub(crate) source_uri: String,
|
|
|
|
|
cache_name: Option<String>,
|
|
|
|
|
pub(crate) api_key: Option<String>,
|
|
|
|
|
cache_mode: TankMode,
|
2023-08-24 21:39:38 +00:00
|
|
|
}
|
|
|
|
|
|
2023-09-01 14:16:01 +00:00
|
|
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
|
2023-08-30 15:53:24 +00:00
|
|
|
pub enum TankMode {
|
2023-08-24 21:39:38 +00:00
|
|
|
Cache,
|
2023-08-30 15:53:24 +00:00
|
|
|
NoCache,
|
2023-08-24 21:39:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FuelTank {
|
2023-08-30 15:53:24 +00:00
|
|
|
pub async fn next_or_fill<T: Debug, U: Debug>(&self) -> ManifoldResult<T>
|
|
|
|
|
where T: DeserializeOwned,
|
|
|
|
|
U: Serialize,
|
|
|
|
|
U: DeserializeOwned, U: Pump<T>
|
|
|
|
|
{
|
2023-09-01 14:16:01 +00:00
|
|
|
let path = PathBuf::new()
|
2023-09-20 22:22:30 +00:00
|
|
|
.join(temp_dir())
|
2023-09-01 14:16:01 +00:00
|
|
|
.join(format!("manifold-{}-cache-{}.json", self.cache_name.clone().unwrap(), CACHE_VERSION));
|
|
|
|
|
|
|
|
|
|
let mut fuel = match self.cache_mode {
|
|
|
|
|
TankMode::Cache => {
|
|
|
|
|
// Read cache only if we have one
|
|
|
|
|
debug!("Filling tank!");
|
|
|
|
|
let mut cache = {
|
|
|
|
|
debug!("Cache path: {:?}", &path);
|
|
|
|
|
fs::OpenOptions::new()
|
|
|
|
|
.read(true)
|
2023-09-20 22:22:30 +00:00
|
|
|
.write(true)
|
2023-09-01 14:16:01 +00:00
|
|
|
.create(true)
|
|
|
|
|
.open(&path)
|
|
|
|
|
.map_err(|e| {
|
|
|
|
|
error!("Error creating cache file: {}", e);
|
|
|
|
|
ManifoldError::from("CACHE ERROR")
|
|
|
|
|
})?
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
debug!("Cache: {:?}", &cache);
|
|
|
|
|
|
|
|
|
|
let json: Result<U, serde_json::Error> = serde_json::from_reader(&mut cache);
|
|
|
|
|
debug!("Json: {:?}", &json);
|
|
|
|
|
match json {
|
|
|
|
|
Ok(contents) => {
|
|
|
|
|
match contents.len() {
|
|
|
|
|
0 => self.emit().await?,
|
|
|
|
|
_ => contents,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => self.emit().await?,
|
2023-08-24 21:39:38 +00:00
|
|
|
}
|
2023-09-01 14:16:01 +00:00
|
|
|
},
|
|
|
|
|
TankMode::NoCache => {
|
|
|
|
|
self.emit().await?
|
|
|
|
|
},
|
2023-08-24 21:39:38 +00:00
|
|
|
};
|
|
|
|
|
|
2023-08-30 15:53:24 +00:00
|
|
|
debug!("Got tips: {:?}", &fuel);
|
|
|
|
|
let single = fuel.pump().ok_or(ManifoldError::from("CACHE ERROR"))?;
|
2023-08-24 21:39:38 +00:00
|
|
|
|
2023-08-30 15:53:24 +00:00
|
|
|
debug!("Got single tip: {:?}", &single);
|
2023-09-01 14:16:01 +00:00
|
|
|
if self.cache_mode == TankMode::Cache {
|
|
|
|
|
// Write cache only if we have one
|
|
|
|
|
let mut cache = {
|
|
|
|
|
debug!("Cache path: {:?}", &path);
|
|
|
|
|
fs::OpenOptions::new()
|
|
|
|
|
.write(true)
|
|
|
|
|
.create(true)
|
|
|
|
|
.open(&path)
|
|
|
|
|
.map_err(|e| {
|
|
|
|
|
error!("Error creating cache file: {}", e);
|
|
|
|
|
ManifoldError::from("CACHE ERROR")
|
|
|
|
|
})?
|
|
|
|
|
};
|
|
|
|
|
cache
|
|
|
|
|
.set_len(0)
|
|
|
|
|
.and_then(|_| cache.seek(SeekFrom::Start(0)))?;
|
|
|
|
|
|
|
|
|
|
debug!("Writing cache");
|
|
|
|
|
serde_json::to_writer(cache, &fuel)?;
|
|
|
|
|
}
|
2023-08-30 15:53:24 +00:00
|
|
|
|
|
|
|
|
return Ok(single)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn emit<U: Debug>(&self) -> ManifoldResult<U>
|
|
|
|
|
where U: DeserializeOwned
|
|
|
|
|
{
|
|
|
|
|
debug!("Getting new tips");
|
|
|
|
|
let result: U;
|
|
|
|
|
|
|
|
|
|
let mut client = Client::new().get(&self.source_uri)
|
2023-08-30 17:44:39 +00:00
|
|
|
.header(CONTENT_TYPE, "application/json")
|
|
|
|
|
.header(ACCEPT, "application/json");
|
|
|
|
|
|
2023-08-30 15:53:24 +00:00
|
|
|
if let Some(api_key) = &self.api_key {
|
|
|
|
|
client = client.header("X-API-KEY", api_key);
|
|
|
|
|
}
|
2023-08-30 17:44:39 +00:00
|
|
|
|
2023-08-30 15:53:24 +00:00
|
|
|
result = client.send().await?.json().await?;
|
|
|
|
|
|
|
|
|
|
debug!("Result: {:?}", &result);
|
2023-08-24 21:39:38 +00:00
|
|
|
|
2023-08-30 15:53:24 +00:00
|
|
|
Ok(result)
|
2023-08-24 21:39:38 +00:00
|
|
|
}
|
|
|
|
|
}
|