use crate::core::error::MonorailError;
use serde::Serialize;
use std::path;
#[derive(Debug)]
pub(crate) struct OutDeleteInput {
pub(crate) all: bool,
}
#[derive(Debug, Serialize)]
pub(crate) struct OutDeleteOutput {
recovered_mb: f64,
}
pub(crate) fn out_delete(
out_dir: &str,
input: &OutDeleteInput,
) -> Result<OutDeleteOutput, MonorailError> {
let recovered_mb = calculate_dir_size_in_mb(out_dir)?;
if input.all {
let tlds = get_tlds(out_dir)?;
for d in tlds.iter() {
std::fs::remove_dir_all(d)?;
}
}
Ok(OutDeleteOutput { recovered_mb })
}
fn calculate_dir_size_in_mb<P: AsRef<path::Path>>(p: P) -> Result<f64, MonorailError> {
let mut total_size: u64 = 0;
let mut stack = vec![p.as_ref().to_path_buf()];
while let Some(current_path) = stack.pop() {
for entry in std::fs::read_dir(current_path)? {
let entry = entry?;
let metadata = entry.metadata()?;
if metadata.is_dir() {
stack.push(entry.path());
} else if metadata.is_file() {
total_size += metadata.len();
}
}
}
Ok(total_size as f64 / (1048576.0)) }
fn get_tlds<P: AsRef<path::Path>>(p: P) -> Result<Vec<path::PathBuf>, MonorailError> {
let mut directories = Vec::new();
for entry in std::fs::read_dir(p)? {
let entry = entry?;
let metadata = entry.metadata()?;
if metadata.is_dir() {
directories.push(entry.path());
}
}
Ok(directories)
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::{self, File};
use std::io::Write;
use tempfile::tempdir;
#[test]
fn test_get_tlds_empty_directory() -> Result<(), MonorailError> {
let dir = tempdir()?;
let top_level_dirs = get_tlds(dir.path())?;
assert!(top_level_dirs.is_empty());
Ok(())
}
#[test]
fn test_get_tlds_with_only_files() -> Result<(), MonorailError> {
let dir = tempdir()?;
File::create(dir.path().join("file1.txt"))?;
File::create(dir.path().join("file2.txt"))?;
let top_level_dirs = get_tlds(dir.path())?;
assert!(top_level_dirs.is_empty());
Ok(())
}
#[test]
fn test_get_tlds_with_subdirectories() -> Result<(), MonorailError> {
let dir = tempdir()?;
let subdir1 = dir.path().join("subdir1");
let subdir2 = dir.path().join("subdir2");
fs::create_dir(&subdir1)?;
fs::create_dir(&subdir2)?;
let mut top_level_dirs = get_tlds(dir.path())?;
top_level_dirs.sort();
assert_eq!(top_level_dirs, vec![subdir1, subdir2]);
Ok(())
}
#[test]
fn test_get_tlds_with_mixed_files_and_directories() -> Result<(), MonorailError> {
let dir = tempdir()?;
let subdir1 = dir.path().join("subdir1");
let subdir2 = dir.path().join("subdir2");
let file1 = dir.path().join("file1.txt");
let file2 = dir.path().join("file2.txt");
fs::create_dir(&subdir1)?;
fs::create_dir(&subdir2)?;
File::create(file1)?;
File::create(file2)?;
let mut top_level_dirs = get_tlds(dir.path())?;
top_level_dirs.sort();
assert_eq!(top_level_dirs, vec![subdir1, subdir2]);
Ok(())
}
#[test]
fn test_size_empty_directory() -> Result<(), MonorailError> {
let dir = tempdir()?;
let size_in_mb = calculate_dir_size_in_mb(dir.path())?;
assert_eq!(size_in_mb, 0.0);
Ok(())
}
#[test]
fn test_size_single_file() -> Result<(), MonorailError> {
let dir = tempdir()?;
let file_path = dir.path().join("file.txt");
let mut file = File::create(file_path)?;
file.write_all(&vec![0u8; 1_048_576])?;
let size_in_mb = calculate_dir_size_in_mb(dir.path())?;
assert!((size_in_mb - 1.0).abs() < f64::EPSILON); Ok(())
}
#[test]
fn test_size_multiple_files() -> Result<(), MonorailError> {
let dir = tempdir()?;
let file1 = dir.path().join("file1.txt");
let file2 = dir.path().join("file2.txt");
let file3 = dir.path().join("file3.txt");
File::create(file1)?.write_all(&vec![0u8; 524_288])?; File::create(file2)?.write_all(&vec![0u8; 262_144])?; File::create(file3)?.write_all(&vec![0u8; 262_144])?;
let size_in_mb = calculate_dir_size_in_mb(dir.path())?;
assert!((size_in_mb - 1.0).abs() < f64::EPSILON); Ok(())
}
#[test]
fn test_size_nested_directories() -> Result<(), MonorailError> {
let dir = tempdir()?;
let sub_dir = dir.path().join("subdir");
fs::create_dir(&sub_dir)?;
let file1 = dir.path().join("file1.txt");
let file2 = sub_dir.join("file2.txt");
File::create(file1)?.write_all(&vec![0u8; 524_288])?; File::create(file2)?.write_all(&vec![0u8; 524_288])?;
let size_in_mb = calculate_dir_size_in_mb(dir.path())?;
assert!((size_in_mb - 1.0).abs() < f64::EPSILON); Ok(())
}
#[test]
fn test_size_large_directory() -> Result<(), MonorailError> {
let dir = tempdir()?;
let file_path = dir.path().join("large_file.txt");
File::create(file_path)?.write_all(&vec![0u8; 5 * 1_048_576])?;
let size_in_mb = calculate_dir_size_in_mb(dir.path())?;
assert!((size_in_mb - 5.0).abs() < f64::EPSILON); Ok(())
}
}