use crate::{Error, Result};
use simple_fs::SPath;
use std::env;
use std::fs::File;
use std::io::{BufReader, Read};
use std::path::Path;
use walkdir::WalkDir;
pub fn current_dir() -> Result<SPath> {
let dir = env::current_dir().map_err(|err| Error::cc("Current dir error", err))?;
let dir = SPath::from_std_path_buf(dir)?;
Ok(dir)
}
pub fn home_dir() -> Result<SPath> {
let home_dir = home::home_dir().ok_or("No Home Dir found")?;
let home_dir: SPath = SPath::from_std_path_buf(home_dir)?;
Ok(home_dir)
}
pub fn list_dirs(base_dir: impl AsRef<Path>, depth: usize, only_leaf: bool) -> Vec<SPath> {
let base_path = base_dir.as_ref();
let base_depth = base_path.components().count();
let mut dirs = Vec::new();
for entry in WalkDir::new(base_path)
.min_depth(if only_leaf { depth } else { 1 })
.max_depth(depth)
.follow_links(true) .into_iter()
.filter_entry(|e| {
e.file_type().is_dir() || (e.path().is_dir() && e.file_type().is_symlink())
}) {
let entry = entry.expect("Error walking directory");
if entry.path().is_dir() {
if only_leaf && depth == 0 && entry.path() == base_path {
continue;
}
let current_depth = entry.path().components().count() - base_depth;
if !only_leaf || current_depth == depth {
if let Ok(spath) = SPath::from_walkdir_entry(entry) {
dirs.push(spath);
}
}
}
}
dirs
}
pub fn is_file_empty(file_path: impl AsRef<Path>) -> Result<bool> {
let path = file_path.as_ref();
let file = File::open(path).map_err(|err| {
Error::cc(
"Cannot determine if file empty",
format!("File '{}' open error. Cause: {err}", path.to_string_lossy()),
)
})?;
let mut reader = BufReader::new(file);
let mut small_buffer = [0; 64];
let num_bytes = reader.read(&mut small_buffer)?;
if num_bytes == 0 {
return Ok(true);
}
if !is_buff_empty(&small_buffer[..num_bytes]) {
return Ok(false);
}
if num_bytes < small_buffer.len() {
return Ok(true);
}
let mut large_buffer = [0; 1024];
loop {
let num_bytes = reader.read(&mut large_buffer)?;
if num_bytes == 0 {
break;
}
if !is_buff_empty(&large_buffer[..num_bytes]) {
return Ok(false);
}
}
Ok(true)
}
fn is_buff_empty(buff: &[u8]) -> bool {
let s = std::str::from_utf8(buff).unwrap_or("");
s.chars().all(|c| c.is_whitespace())
}
#[allow(unused)]
pub fn safer_trash_file(path: &SPath) -> Result<bool> {
if !path.exists() {
return Ok(false);
}
if !path.is_file() {
return Err(format!("Path '{path}' is not a file. Cannot delete with safer_delete_file.").into());
}
trash::delete(path).map_err(|err| format!("Cannot delete file '{path}'. Cause: {err}"))?;
Ok(true)
}
pub fn safer_delete_dir(path: &SPath) -> Result<bool> {
if !path.exists() {
return Ok(false);
}
if !path.is_dir() {
return Err(format!("Path '{path}' is not a directory. Cannot delete with safer_delete_dir.").into());
}
trash::delete(path).map_err(|err| format!("Cannot delete dir '{path}'. Cause: {err}"))?;
Ok(true)
}
#[cfg(test)]
mod tests {
type Result<T> = core::result::Result<T, Box<dyn std::error::Error>>;
use super::*;
#[test]
fn test_support_files_list_dirs_only_leaf() -> Result<()> {
let base_dir = "src";
let depth = 2;
let only_leaf = true;
let dirs = list_dirs(base_dir, depth, only_leaf);
let expected = vec![
"src/script/lua_script",
"src/support/code",
"src/support/md",
"src/support/text",
];
for exp in expected {
let exp_path = SPath::new(exp).canonicalize()?;
let found = dirs
.iter()
.any(|d| d.canonicalize().map(|p| p.as_str() == exp_path.as_str()).unwrap_or(false));
assert!(found, "Expected directory {:?} not found in the returned list", exp);
}
Ok(())
}
#[test]
fn test_support_files_list_dirs_all() -> Result<()> {
let base_dir = "src";
let depth = 2;
let only_leaf = false;
let dirs = list_dirs(base_dir, depth, only_leaf);
let expected = vec!["src/agent", "src/cli", "src/script", "src/support", "src/script/lua_script"];
for exp in expected {
let exp_path = SPath::new(exp).canonicalize()?;
let found = dirs
.iter()
.any(|d| d.canonicalize().map(|p| p.as_str() == exp_path.as_str()).unwrap_or(false));
assert!(found, "Expected directory {:?} not found in the returned list", exp);
}
Ok(())
}
#[test]
fn test_support_files_list_dirs_depth_zero() -> Result<()> {
let base_dir = "src";
let depth = 0;
let only_leaf = true;
let dirs = list_dirs(base_dir, depth, only_leaf);
assert!(dirs.is_empty(), "Expected empty directory list for depth=0");
Ok(())
}
}