2023-08-24 21:39:38 +00:00
|
|
|
use std::fmt::{Display, Formatter};
|
|
|
|
|
use std::fs;
|
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
use dirs::home_dir;
|
|
|
|
|
use std::io::{SeekFrom, Seek};
|
|
|
|
|
use reqwest::Client;
|
|
|
|
|
use reqwest::header::CONTENT_TYPE;
|
|
|
|
|
|
|
|
|
|
use crate::error::{ManifoldError, ManifoldResult};
|
|
|
|
|
|
|
|
|
|
const CACHE_VERSION: &'static str = "1";
|
|
|
|
|
|
|
|
|
|
pub trait Pump<V> {
|
|
|
|
|
fn pump<F>(self) -> Result<V, ManifoldError>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
pub struct FuelTank {
|
|
|
|
|
mode: TankMode,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
|
|
|
enum TankMode {
|
|
|
|
|
Cache,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FuelTank {
|
|
|
|
|
pub fn with_cache() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
mode: TankMode::Cache,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Serialize, Deserialize, Debug)]
|
|
|
|
|
pub struct Tip {
|
|
|
|
|
pub number: i32,
|
|
|
|
|
pub tip: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for Tip {
|
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
write!(f, "#{}: {}", self.number, self.tip)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
|
pub struct Croak {
|
|
|
|
|
pub tips: Vec<Tip>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn croak() -> ManifoldResult<Vec<Tip>> {
|
|
|
|
|
|
|
|
|
|
debug!("Getting new tips");
|
|
|
|
|
let result: Croak;
|
|
|
|
|
|
|
|
|
|
let client = Client::new();
|
|
|
|
|
result = client.get("https://frog.tips/api/1/tips/")
|
|
|
|
|
.header(CONTENT_TYPE, "application/json")
|
2023-08-24 21:42:04 +00:00
|
|
|
.send().await?.json().await?;
|
2023-08-24 21:39:38 +00:00
|
|
|
|
|
|
|
|
debug!("Result: {:?}", &result);
|
|
|
|
|
|
|
|
|
|
Ok(result.tips)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FuelTank {
|
|
|
|
|
pub async fn next_or_fill(self) -> Result<Tip, ManifoldError> {
|
|
|
|
|
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!("coffeebot-frog-cache-{}.json", 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<Vec<Tip>, serde_json::Error> = serde_json::from_reader(&mut cache);
|
|
|
|
|
debug!("Json: {:?}", &json);
|
|
|
|
|
let mut tips = match json {
|
|
|
|
|
Ok(contents) => {
|
|
|
|
|
match contents.len() {
|
|
|
|
|
0 => croak().await?,
|
|
|
|
|
_ => contents,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => croak().await?,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
debug!("Got tips: {:?}", &tips);
|
|
|
|
|
let tip = tips.pop().ok_or(ManifoldError::from("FROG HAS NO TIPS FOR YOU. CROAK ERROR 12"))?;
|
|
|
|
|
|
|
|
|
|
debug!("Got single tip: {:?}", &tip);
|
|
|
|
|
cache
|
|
|
|
|
.set_len(0)
|
|
|
|
|
.and_then(|_| cache.seek(SeekFrom::Start(0)))?;
|
|
|
|
|
|
|
|
|
|
debug!("Writing cache");
|
|
|
|
|
serde_json::to_writer(cache, &tips)?;
|
|
|
|
|
|
|
|
|
|
Ok(tip)
|
|
|
|
|
}
|
|
|
|
|
}
|