use anyhow::anyhow;
use codec::{Decode, Encode};
use jam_types::WorkItem;
#[derive(Encode, Decode, Debug, Default)]
struct QueueState {
items: Vec<WorkItem>,
}
fn with_persistent<T: Encode + Decode, R>(
name: &str,
f: impl FnOnce(&mut Option<T>) -> R,
) -> anyhow::Result<R> {
let dirs = directories::ProjectDirs::from("io", "parity", "jam")
.ok_or(anyhow!("This platform has no place for persistent data"))?;
let dir = dirs.cache_dir();
if !dir.exists() {
std::fs::create_dir_all(dir)?;
}
let path = dir.join(name);
let mut state = match std::fs::read(&path) {
Ok(data) => Some(T::decode(&mut &data[..])?),
Err(ref e) if e.kind() == std::io::ErrorKind::NotFound => None,
Err(e) => return Err(e.into()),
};
let r = f(&mut state);
if let Some(s) = state {
let new_path = format!(".{name}.new");
let old_path = format!("{name}.old");
std::fs::write(&new_path, s.encode())?;
std::fs::remove_file(&old_path).ok_if_not_found()?;
std::fs::rename(&path, &old_path).ok_if_not_found()?;
std::fs::rename(&new_path, &path)?;
} else {
std::fs::remove_file(&path).ok_if_not_found()?;
}
Ok(r)
}
pub fn push(item: WorkItem) -> anyhow::Result<usize> {
with_persistent::<QueueState, usize>("queue", |queue| {
let mut q = queue.take().unwrap_or_default();
q.items.push(item);
let r = q.items.len();
*queue = Some(q);
r
})
}
pub fn peek() -> anyhow::Result<Vec<WorkItem>> {
with_persistent::<QueueState, _>("queue", |queue| {
queue.as_ref().map(|q| q.items.clone()).unwrap_or_default()
})
}
pub fn take() -> anyhow::Result<Vec<WorkItem>> {
with_persistent::<QueueState, _>("queue", |queue| queue.take().unwrap_or_default().items)
}
pub fn queue_status() -> anyhow::Result<()> {
match peek()?.len() {
0 => println!("Queue is empty"),
1 => println!("Queue has 1 item"),
x => println!("Queue has {x} items"),
}
Ok(())
}
trait OkIfNotFound {
fn ok_if_not_found(self) -> Self;
}
impl OkIfNotFound for std::io::Result<()> {
fn ok_if_not_found(self) -> Self {
match self {
Err(ref e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
other => other,
}
}
}