use std::any::Any;
use std::convert::Infallible;
use futures::Future;
use serde::{Deserialize, Serialize};
use tokio::fs::{create_dir_all, File};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
pub async fn cache_res<D, F, Ft>(name: &str, f: F, force_run: bool) -> D
where
D: Serialize + for<'de> Deserialize<'de> + Any,
F: Fn() -> Ft,
Ft: Future<Output = D>,
{
let f_res = || async { Ok::<D, Infallible>(f().await) };
cache_fallible_res(name, f_res, force_run).await.unwrap()
}
pub async fn cache_fallible_res<D, E, F, Ft>(name: &str, f: F, force_run: bool) -> Result<D, E>
where
D: Serialize + for<'de> Deserialize<'de>,
E: std::error::Error,
F: Fn() -> Ft,
Ft: Future<Output = Result<D, E>>,
{
let name = name.replace('/', "-");
if cfg!(debug_assertions) {
let filename = format!("dist/cache/{}.json", &name);
match File::open(&filename).await {
Ok(mut file) => {
if force_run {
let res = f().await?;
let str_res = serde_json::to_string(&res).unwrap_or_else(|err| {
panic!(
"couldn't serialize result of entry '{}' for caching: {}",
&filename, err
)
});
let mut file = File::create(&filename).await.unwrap_or_else(|err| {
panic!(
"couldn't create cache file for entry '{}': {}",
&filename, err
)
});
file.write_all(str_res.as_bytes())
.await
.unwrap_or_else(|err| {
panic!(
"couldn't write cache to file for entry '{}': {}",
&filename, err
)
});
Ok(res)
} else {
let mut contents = String::new();
file.read_to_string(&mut contents)
.await
.unwrap_or_else(|err| {
panic!(
"couldn't read cache from file for entry '{}': {}",
&filename, err
)
});
let res = match serde_json::from_str(&contents) {
Ok(cached_res) => cached_res,
Err(_) => {
let res = f().await?;
let str_res = serde_json::to_string(&res).unwrap_or_else(|err| {
panic!(
"couldn't serialize result of entry '{}' for caching: {}",
&filename, err
)
});
let mut file = File::create(&filename).await.unwrap_or_else(|err| {
panic!(
"couldn't create cache file for entry '{}': {}",
&filename, err
)
});
file.write_all(str_res.as_bytes())
.await
.unwrap_or_else(|err| {
panic!(
"couldn't write cache to file for entry '{}': {}",
&filename, err
)
});
res
}
};
Ok(res)
}
}
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
create_dir_all("dist/cache")
.await
.unwrap_or_else(|err| panic!("couldn't create cache directory: {}", err));
let res = f().await?;
let str_res = serde_json::to_string(&res).unwrap_or_else(|err| {
panic!(
"couldn't serialize result of entry '{}' for caching: {}",
&filename, err
)
});
let mut file = File::create(&filename).await.unwrap_or_else(|err| {
panic!(
"couldn't create cache file for entry '{}': {}",
&filename, err
)
});
file.write_all(str_res.as_bytes())
.await
.unwrap_or_else(|err| {
panic!(
"couldn't write cache to file for entry '{}': {}",
&filename, err
)
});
Ok(res)
}
Err(err) => panic!(
"filesystem error occurred while trying to read cache file for entry '{}': {}",
&filename, err
),
}
} else {
f().await
}
}