use std::{
fs, io,
path::{Path, PathBuf},
sync::atomic::{AtomicU64, Ordering},
};
use ic_host_fs::{durable::write_bytes, read::read_file};
use super::cache_fs::remove_path_if_present;
const STAGE_ATTEMPTS: usize = 64;
static STAGE_SEQUENCE: AtomicU64 = AtomicU64::new(0);
#[derive(Debug)]
pub struct ArtifactStagingDirectory {
root: PathBuf,
armed: bool,
}
impl ArtifactStagingDirectory {
pub fn new(parent: &Path) -> io::Result<Self> {
for _ in 0..STAGE_ATTEMPTS {
let sequence = STAGE_SEQUENCE.fetch_add(1, Ordering::Relaxed);
let root = parent.join(format!(
".ic-build-artifact-stage-{}-{sequence}",
std::process::id()
));
match fs::create_dir(&root) {
Ok(()) => return Ok(Self { root, armed: true }),
Err(source) if source.kind() == io::ErrorKind::AlreadyExists => {}
Err(source) => return Err(source),
}
}
Err(io::Error::new(
io::ErrorKind::AlreadyExists,
format!(
"failed to allocate artifact staging beneath {}",
parent.display()
),
))
}
#[must_use]
pub fn path(&self) -> &Path {
&self.root
}
pub fn close(mut self) -> io::Result<()> {
self.armed = false;
remove_path_if_present(&self.root)
}
}
impl Drop for ArtifactStagingDirectory {
fn drop(&mut self) {
if self.armed {
let _ = remove_path_if_present(&self.root);
}
}
}
#[derive(Debug)]
pub struct CapturedArtifact {
staging: ArtifactStagingDirectory,
path: PathBuf,
}
impl CapturedArtifact {
pub fn capture(source: &Path, parent: &Path, maximum_bytes: usize) -> io::Result<Self> {
let bytes = read_file(source, maximum_bytes).map_err(io::Error::from)?;
let staging = ArtifactStagingDirectory::new(parent)?;
let path = staging.path().join("input.artifact");
write_bytes(&path, &bytes).map_err(io::Error::other)?;
Ok(Self { staging, path })
}
#[must_use]
pub fn path(&self) -> &Path {
&self.path
}
pub fn close(self) -> io::Result<()> {
self.staging.close()
}
}
#[cfg(test)]
mod tests;