use crate::error::{Error, ensure};
use crate::{
disk_plan::DiskPlan,
node::Node,
publish::{PublishReport, ensure_journal_distinct},
};
use std::{
fs::{File, OpenOptions},
io::Write,
path::Path,
};
pub(crate) fn guard(plan: &DiskPlan, journal: &Path) -> Result<(), Error> {
let observations = journal.with_extension("observations.jsonl");
for name in ["plan.json", "records.bin", "index.bin"] {
let input = plan.directory().join(name);
ensure_journal_distinct(&input, journal)?;
ensure_journal_distinct(&input, &observations)?;
}
if journal.try_exists()? {
ensure_journal_distinct(journal, &observations)?;
} else {
ensure!(
journal != observations,
"journal and observations log must differ"
);
}
if observations.try_exists()? {
ensure!(
std::fs::symlink_metadata(&observations)?
.file_type()
.is_file(),
"observations log must be a regular file"
);
}
Ok(())
}
pub(crate) fn observe(node: &Node, journal: &Path, report: &PublishReport) -> Result<(), Error> {
use std::os::unix::fs::OpenOptionsExt;
let path = journal.with_extension("observations.jsonl");
let mut file = OpenOptions::new()
.create(true)
.append(true)
.mode(0o600)
.open(&path)?;
let mut bytes = serde_json::to_vec(
&serde_json::json!({"source": node.inclusion_evidence(), "chain": node.chain(), "report": report}),
)?;
bytes.push(b'\n');
file.write_all(&bytes)?;
file.sync_all()?;
File::open(urma_io::output_parent(&path))?.sync_all()?;
Ok(())
}