use anyhow::{Context, Result};
use std::fs;
use std::path::Path;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum PalaceError {
#[error("Insufficient memory: required {required} bytes, available {available} bytes")]
InsufficientMemory { required: u64, available: u64 },
#[error("Invalid mount point: {0}")]
InvalidMountPoint(String),
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
}
pub fn get_available_memory() -> Result<u64> {
let meminfo = fs::read_to_string("/proc/meminfo")?;
let available = meminfo
.lines()
.find(|line| line.starts_with("MemAvailable:"))
.and_then(|line| line.split_whitespace().nth(1))
.and_then(|value| value.parse::<u64>().ok())
.context("Failed to parse available memory")?;
Ok(available * 1024) }
pub fn get_directory_size(path: &Path) -> Result<u64> {
let metadata = fs::metadata(path)?;
if metadata.is_file() {
return Ok(metadata.len());
}
let mut total_size = 0;
for entry in fs::read_dir(path)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
total_size += get_directory_size(&path)?;
} else {
total_size += entry.metadata()?.len();
}
}
Ok(total_size)
}
pub fn check_mount_point(mount_point: &Path) -> Result<()> {
if !mount_point.starts_with("/mnt") {
return Err(PalaceError::InvalidMountPoint(mount_point.to_string_lossy().into_owned()).into());
}
Ok(())
}
pub fn ensure_directory(path: &Path) -> Result<()> {
if !path.exists() {
fs::create_dir_all(path).context("Failed to create directory")?;
}
Ok(())
}
pub fn check_memory_availability(required: u64, threshold_percentage: u8) -> Result<()> {
let available = get_available_memory()?;
let threshold = (available as f64 * (threshold_percentage as f64 / 100.0)) as u64;
if required > threshold {
Err(PalaceError::InsufficientMemory { required, available: threshold }.into())
} else {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
#[test]
fn test_get_directory_size() -> Result<()> {
let dir = tempdir()?;
let file_path = dir.path().join("test_file.txt");
fs::write(&file_path, "Hello, world!")?;
let size = get_directory_size(dir.path())?;
assert_eq!(size, 13); Ok(())
}
#[test]
fn test_check_mount_point() {
assert!(check_mount_point(Path::new("/mnt/dataset/test")).is_ok());
assert!(check_mount_point(Path::new("/tmp/test")).is_err());
}
#[test]
fn test_ensure_directory() -> Result<()> {
let dir = tempdir()?;
let test_dir = dir.path().join("test_dir");
ensure_directory(&test_dir)?;
assert!(test_dir.exists());
Ok(())
}
}