use serde::{Deserialize, Serialize};
pub mod paths {
pub const UENV_MOUNT_BASE: &str = "/run/pact/uenv";
pub const WORKDIR_BASE: &str = "/run/pact/workdir";
pub const DATA_STAGE_BASE: &str = "/run/pact/data";
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MountHandle {
pub image_path: String,
pub mount_point: String,
}
pub const DEFAULT_HOLD_TIME_SECS: u64 = 60;
pub trait MountManager: Send + Sync {
fn acquire_mount(&self, image_path: &str) -> Result<MountHandle, MountError>;
fn release_mount(&self, handle: &MountHandle) -> Result<(), MountError>;
fn force_unmount(&self, image_path: &str) -> Result<(), MountError>;
fn reconstruct_state(&self, active_allocations: &[String]) -> Result<(), MountError>;
}
#[derive(Debug, thiserror::Error)]
pub enum MountError {
#[error("mount failed for {image_path}: {reason}")]
MountFailed { image_path: String, reason: String },
#[error("unmount failed for {mount_point}: {reason}")]
UnmountFailed { mount_point: String, reason: String },
#[error("refcount inconsistency for {image_path}: {detail}")]
RefcountInconsistency { image_path: String, detail: String },
#[error("mount I/O error: {0}")]
Io(#[from] std::io::Error),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mount_handle_serialization() {
let handle = MountHandle {
image_path: "/images/pytorch-2.5.sqfs".to_string(),
mount_point: "/run/pact/uenv/pytorch-2.5".to_string(),
};
let json = serde_json::to_string(&handle).unwrap();
let deser: MountHandle = serde_json::from_str(&json).unwrap();
assert_eq!(deser.image_path, "/images/pytorch-2.5.sqfs");
assert_eq!(deser.mount_point, "/run/pact/uenv/pytorch-2.5");
}
#[test]
fn well_known_paths() {
assert!(paths::UENV_MOUNT_BASE.starts_with("/run/pact/"));
assert!(paths::WORKDIR_BASE.starts_with("/run/pact/"));
assert!(paths::DATA_STAGE_BASE.starts_with("/run/pact/"));
}
}