use std::error::Error;
use std::fs::{self, File};
use std::io::BufReader;
use anyhow::anyhow;
use reqwest::Client;
use lesspub_query::{get_ea_post, get_lw_post, get_lw_sequence};
use lesspub_schema::{Post, Sequence};
use crate::cli::CacheOptions;
pub fn cache_data<C: AsRef<[u8]>>(
subdirectory: Option<&str>,
filename: &str,
data: C,
cache_options: &CacheOptions,
) -> Result<(), Box<dyn Error>> {
let dir_path = match subdirectory {
Some(subdir) => cache_options.cache_dir.join(subdir),
None => cache_options.cache_dir.clone(),
};
fs::create_dir_all(&dir_path)?;
fs::write(dir_path.join(filename), data)?;
Ok(())
}
pub fn cache_crosspost(
post: &Post,
id: &str,
cache_options: &CacheOptions,
) -> Result<(), Box<dyn Error>> {
let slug = slug::slugify(id);
cache_data(
None,
&format!("post_{slug}.json"),
serde_json::to_string_pretty(post)?.as_bytes(),
cache_options,
)
}
pub fn uncache_crosspost(
id: &str,
cache_options: &CacheOptions,
) -> Result<Option<Post>, Box<dyn Error>> {
let slug = slug::slugify(id);
if cache_options.ignore_cache {
Ok(None)
} else if let Ok(file) = File::open(cache_options.cache_dir.join(format!("post_{slug}.json"))) {
let post: Post = serde_json::from_reader(BufReader::new(file))?;
Ok(Some(post))
} else {
Ok(None)
}
}
pub async fn get_crosspost(
client: &Client,
id: &str,
cache_options: &CacheOptions,
) -> Result<Post, Box<dyn Error>> {
match uncache_crosspost(id, cache_options) {
Ok(Some(cached_post)) => {
log::debug!("Cached post pulled");
Ok(cached_post)
}
_ => {
log::debug!("Downloading post");
let post = get_ea_post(client, id).await?;
cache_crosspost(&post, id, cache_options)?;
Ok(post)
}
}
}
pub fn cache_post(
post: &Post,
id: &str,
cache_options: &CacheOptions,
) -> Result<(), Box<dyn Error>> {
let slug = slug::slugify(id);
cache_data(
None,
&format!("lw_post_{slug}.json"),
serde_json::to_string_pretty(post)?.as_bytes(),
cache_options,
)
}
pub fn uncache_post(
id: &str,
cache_options: &CacheOptions,
) -> Result<Option<Post>, Box<dyn Error>> {
let slug = slug::slugify(id);
if cache_options.ignore_cache {
Ok(None)
} else if let Ok(file) =
File::open(cache_options.cache_dir.join(format!("lw_post_{slug}.json")))
{
let post: Post = serde_json::from_reader(BufReader::new(file))?;
Ok(Some(post))
} else {
Ok(None)
}
}
pub async fn get_post(
client: &Client,
id: &str,
cache_options: &CacheOptions,
) -> Result<Post, Box<dyn Error>> {
match uncache_post(id, cache_options) {
Ok(Some(cached_post)) => {
log::debug!("Cached post pulled");
Ok(cached_post)
}
_ => {
log::debug!("Downloading post");
let post = get_lw_post(client, id).await?;
cache_post(&post, id, cache_options)?;
Ok(post)
}
}
}
pub fn cache_sequence(
sequence: &Sequence,
cache_options: &CacheOptions,
) -> Result<(), Box<dyn Error>> {
let slug = slug::slugify(sequence._id.clone().ok_or(anyhow!("no sequence id"))?);
cache_data(
None,
&format!("{slug}.json"),
serde_json::to_string_pretty(sequence)?.as_bytes(),
cache_options,
)
}
pub fn uncache_sequence(
sequence_info: &lesspub_schema::SequenceListInfo,
cache_options: &CacheOptions,
) -> Result<Option<Sequence>, Box<dyn Error>> {
let id = sequence_info._id.clone().ok_or("no id in sequence")?;
let slug = slug::slugify(&id);
if cache_options.ignore_cache {
Ok(None)
} else if let Ok(file) = File::open(cache_options.cache_dir.join(format!("{slug}.json"))) {
let sequence: Sequence = serde_json::from_reader(BufReader::new(file))?;
if sequence_info.postsCount.is_some_and(|new_count| {
sequence
.postsCount
.is_none_or(|old_count| new_count > old_count)
}) || sequence_info.lastUpdated.is_some_and(|new_date| {
sequence
.lastUpdated
.is_none_or(|old_date| new_date > old_date)
}) {
Ok(None)
} else {
Ok(Some(sequence))
}
} else {
Ok(None)
}
}
pub async fn get_sequence(
client: &Client,
sequence_info: &lesspub_schema::SequenceListInfo,
cache_options: &CacheOptions,
) -> Result<Sequence, Box<dyn Error>> {
match uncache_sequence(sequence_info, cache_options) {
Ok(Some(cached_sequence)) => {
log::debug!("Cached sequence pulled");
Ok(cached_sequence)
}
_ => {
log::debug!("Downloading sequence");
let id = sequence_info._id.clone().ok_or("no id in sequence")?;
let sequence = get_lw_sequence(client, &id).await?;
cache_sequence(&sequence, cache_options)?;
Ok(sequence)
}
}
}