use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;
pub(crate) const ACTIVE_FILE: &str = "subnet.active.json";
#[derive(Debug)]
pub(crate) struct Attachments {
path: PathBuf,
by_verifier: BTreeMap<u64, String>,
}
pub(crate) type SharedAttachments = Arc<parking_lot::Mutex<Attachments>>;
impl Attachments {
pub(crate) fn load(state_root: &Path) -> Result<Self, String> {
let path = state_root.join(ACTIVE_FILE);
let by_verifier = match std::fs::read(&path) {
Ok(bytes) => {
let raw: BTreeMap<String, String> = serde_json::from_slice(&bytes)
.map_err(|e| format!("{}: {e}", path.display()))?;
raw.into_iter()
.map(|(k, v)| {
u64::from_str_radix(k.trim_start_matches("0x"), 16)
.map(|node| (node, v))
.map_err(|_| format!("{}: bad verifier id {k}", path.display()))
})
.collect::<Result<_, _>>()?
}
Err(e) if e.kind() == std::io::ErrorKind::NotFound => BTreeMap::new(),
Err(e) => return Err(format!("{}: {e}", path.display())),
};
Ok(Self { path, by_verifier })
}
pub(crate) fn active(&self, verifier: u64) -> Option<&str> {
self.by_verifier.get(&verifier).map(String::as_str)
}
pub(crate) fn is_active(&self, verifier: u64, scope: &str) -> bool {
self.active(verifier) == Some(scope)
}
pub(crate) fn set(&mut self, verifier: u64, scope: &str) -> std::io::Result<()> {
let previous = self.by_verifier.insert(verifier, scope.to_string());
if let Err(e) = self.persist() {
match previous {
Some(p) => self.by_verifier.insert(verifier, p),
None => self.by_verifier.remove(&verifier),
};
return Err(e);
}
Ok(())
}
pub(crate) fn activate_if_vacant(
&mut self,
verifier: u64,
scope: &str,
) -> std::io::Result<bool> {
match self.active(verifier) {
Some(active) => Ok(active == scope),
None => self.set(verifier, scope).map(|()| true),
}
}
pub(crate) fn clear_if(&mut self, verifier: u64, scope: &str) -> std::io::Result<bool> {
if !self.is_active(verifier, scope) {
return Ok(false);
}
let removed = self.by_verifier.remove(&verifier);
if let Err(e) = self.persist() {
if let Some(r) = removed {
self.by_verifier.insert(verifier, r);
}
return Err(e);
}
Ok(true)
}
fn persist(&self) -> std::io::Result<()> {
let raw: BTreeMap<String, &String> = self
.by_verifier
.iter()
.map(|(k, v)| (format!("0x{k:016x}"), v))
.collect();
let bytes = serde_json::to_vec_pretty(&raw).map_err(std::io::Error::other)?;
let tmp = self.path.with_extension("tmp");
std::fs::write(&tmp, bytes)?;
std::fs::rename(&tmp, &self.path)
}
}