use std::borrow::Cow;
use std::ffi::OsStr;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use ccp_shared::proof::CCProof;
use crate::proof_storage::ensure_dir;
const EXPECT_DEFAULT_SERIALIZER: &str = "the default serde serializer shouldn't fail";
#[derive(Debug)]
pub struct ProofStorage {
proof_directory: PathBuf,
}
impl ProofStorage {
pub fn new(proof_directory: PathBuf) -> Self {
Self { proof_directory }
}
pub async fn store_new_proof(&self, proof: CCProof) -> tokio::io::Result<()> {
ensure_dir(&self.proof_directory).await?;
let proof_as_string = serde_json::to_string(&proof).expect(EXPECT_DEFAULT_SERIALIZER);
let proof_path = self.proof_directory.join(proof.id.idx.to_string());
tokio::task::spawn_blocking(move || save_reliably(&proof_path, proof_as_string)).await??;
Ok(())
}
}
pub(crate) fn save_reliably(path: &Path, contents: impl AsRef<[u8]>) -> std::io::Result<()> {
let base_dir = path.parent().map(Cow::Borrowed).unwrap_or_default();
let base_dir_file = File::open(&base_dir)?;
let (mut draft_file, draft_path) =
gen_draft_file(&base_dir, path.file_name().unwrap_or_default())?;
draft_file.write_all(contents.as_ref())?;
draft_file.flush()?;
draft_file.sync_all()?;
std::mem::drop(draft_file);
std::fs::rename(draft_path, path)?;
base_dir_file.sync_all()?;
Ok(())
}
fn gen_draft_file(base_dir: &Path, prefix: &OsStr) -> std::io::Result<(File, PathBuf)> {
let named_tmp_file = tempfile::Builder::new()
.prefix(prefix)
.tempfile_in(base_dir)?;
Ok(named_tmp_file.keep()?)
}