// SPDX-License-Identifier: AGPL-3.0-or-later use anyhow::{Context, Result}; use serde::Serialize; use sha2::{Digest, Sha256}; use std::fs; use std::io::{self, Read}; use std::path::Path; pub(crate) fn remove_file_if_exists(path: &Path) -> Result<()> { match fs::remove_file(path) { Ok(()) => Ok(()), Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(()), Err(error) => Err(error).with_context(|| format!("Failed to remove {}", path.display())), } } pub(crate) fn remove_dir_if_exists(path: &Path) -> Result<()> { match fs::remove_dir_all(path) { Ok(()) => Ok(()), Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(()), Err(error) => Err(error).with_context(|| format!("Failed to remove {}", path.display())), } } pub(crate) fn write_json_pretty(path: &Path, value: &T) -> Result<()> { if let Some(parent) = path.parent() { fs::create_dir_all(parent) .with_context(|| format!("Failed to create {}", parent.display()))?; } let mut bytes = serde_json::to_vec_pretty(value)?; bytes.push(b'\n'); fs::write(path, bytes).with_context(|| format!("Failed to write {}", path.display())) } pub(crate) fn sha256_reader(mut reader: impl Read) -> io::Result { let mut hasher = Sha256::new(); let mut buffer = [0u8; 64 * 1024]; loop { let read = reader.read(&mut buffer)?; if read == 0 { break; } hasher.update(&buffer[..read]); } Ok(hex::encode(hasher.finalize())) } pub(crate) fn sha256_file(path: &Path) -> Result { let file = fs::File::open(path).with_context(|| format!("Failed to open {}", path.display()))?; sha256_reader(file).with_context(|| format!("Failed to read {}", path.display())) }