use std::fs::{self, OpenOptions};
use std::io::Write;
use std::path::{Path, PathBuf};
use walkdir::WalkDir;
use crate::error::{MemoryError, Result};
pub struct FileManager {
base_dir: PathBuf,
}
impl FileManager {
pub fn new(base_dir: PathBuf) -> Result<Self> {
if !base_dir.exists() {
fs::create_dir_all(&base_dir)?;
fs::create_dir_all(base_dir.join("system"))?;
}
Ok(Self { base_dir })
}
pub fn write_file(&self, relative_path: &str, content: &str) -> Result<()> {
let full_path = self.base_dir.join(relative_path);
if let Some(parent) = full_path.parent() {
fs::create_dir_all(parent)?;
}
fs::write(full_path, content)?;
Ok(())
}
pub fn append_to_file(&self, relative_path: &str, content: &str) -> Result<()> {
let full_path = self.base_dir.join(relative_path);
if let Some(parent) = full_path.parent() {
fs::create_dir_all(parent)?;
}
let mut file = OpenOptions::new()
.create(true)
.append(true)
.open(full_path)?;
writeln!(file, "{}", content)?;
Ok(())
}
pub fn read_file(&self, relative_path: &str) -> Result<String> {
let full_path = self.base_dir.join(relative_path);
if !full_path.exists() {
return Err(MemoryError::FileNotFound(relative_path.to_string()));
}
Ok(fs::read_to_string(full_path)?)
}
pub fn get_tree(&self) -> String {
let mut tree = String::new();
for entry in WalkDir::new(&self.base_dir)
.sort_by_file_name()
.into_iter()
.filter_map(|e| e.ok()) {
let path = entry.path();
let relative = path.strip_prefix(&self.base_dir).unwrap_or(path);
let depth = entry.depth();
if depth == 0 {
tree.push_str(&format!("{}/\n", self.base_dir.display()));
continue;
}
let indent = " ".repeat(depth);
let name = relative.file_name().unwrap_or_default().to_string_lossy();
if entry.file_type().is_dir() {
tree.push_str(&format!("{}{} /\n", indent, name));
} else {
tree.push_str(&format!("{}{} \n", indent, name));
}
}
tree
}
pub fn search(&self, pattern: &str) -> Result<Vec<(String, String)>> {
let mut results = Vec::new();
for entry in WalkDir::new(&self.base_dir)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.file_type().is_file()) {
let content = fs::read_to_string(entry.path())?;
if content.contains(pattern) {
let relative = entry.path().strip_prefix(&self.base_dir)
.unwrap_or(entry.path())
.to_string_lossy()
.to_string();
results.push((relative, content));
}
}
Ok(results)
}
pub fn base_path(&self) -> &Path {
&self.base_dir
}
}