Skip to main content

teaql_tool_std/
file.rs

1use std::fs;
2use std::path::{Path, PathBuf};
3use teaql_tool_core::{Result, TeaQLToolError};
4
5/// File utility wrapper
6pub struct FileTool;
7
8impl FileTool {
9    /// Read the entire contents of a file into a string
10    pub fn read_string<P: AsRef<Path>>(&self, path: P) -> Result<String> {
11        fs::read_to_string(path.as_ref())
12            .map_err(|e| TeaQLToolError::ExecutionError(format!("Failed to read file: {}", e)))
13    }
14
15    /// Read the entire contents of a file into a bytes vector
16    pub fn read_bytes<P: AsRef<Path>>(&self, path: P) -> Result<Vec<u8>> {
17        fs::read(path.as_ref())
18            .map_err(|e| TeaQLToolError::ExecutionError(format!("Failed to read bytes: {}", e)))
19    }
20
21    /// Write a string to a file (creates or overwrites)
22    pub fn write_string<P: AsRef<Path>, C: AsRef<[u8]>>(&self, path: P, content: C) -> Result<()> {
23        fs::write(path.as_ref(), content)
24            .map_err(|e| TeaQLToolError::ExecutionError(format!("Failed to write file: {}", e)))
25    }
26
27    /// Check if a path exists
28    pub fn exists<P: AsRef<Path>>(&self, path: P) -> bool {
29        path.as_ref().exists()
30    }
31
32    /// Check if a path is a file
33    pub fn is_file<P: AsRef<Path>>(&self, path: P) -> bool {
34        path.as_ref().is_file()
35    }
36
37    /// Check if a path is a directory
38    pub fn is_dir<P: AsRef<Path>>(&self, path: P) -> bool {
39        path.as_ref().is_dir()
40    }
41
42    /// Create a single directory (fails if parent doesn't exist)
43    pub fn mkdir<P: AsRef<Path>>(&self, path: P) -> Result<()> {
44        fs::create_dir(path.as_ref())
45            .map_err(|e| TeaQLToolError::ExecutionError(format!("Failed to create directory: {}", e)))
46    }
47
48    /// Recursively create a directory and all of its parent components if they are missing
49    pub fn mkdir_all<P: AsRef<Path>>(&self, path: P) -> Result<()> {
50        fs::create_dir_all(path.as_ref())
51            .map_err(|e| TeaQLToolError::ExecutionError(format!("Failed to create directories recursively: {}", e)))
52    }
53
54    /// Delete a file
55    pub fn delete_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
56        fs::remove_file(path.as_ref())
57            .map_err(|e| TeaQLToolError::ExecutionError(format!("Failed to delete file: {}", e)))
58    }
59
60    /// Delete an empty directory
61    pub fn delete_dir<P: AsRef<Path>>(&self, path: P) -> Result<()> {
62        fs::remove_dir(path.as_ref())
63            .map_err(|e| TeaQLToolError::ExecutionError(format!("Failed to delete directory: {}", e)))
64    }
65
66    /// Recursively delete a directory and all of its contents
67    pub fn delete_recursive<P: AsRef<Path>>(&self, path: P) -> Result<()> {
68        fs::remove_dir_all(path.as_ref())
69            .map_err(|e| TeaQLToolError::ExecutionError(format!("Failed to recursively delete directory: {}", e)))
70    }
71
72    /// Copy a file
73    pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(&self, from: P, to: Q) -> Result<u64> {
74        fs::copy(from.as_ref(), to.as_ref())
75            .map_err(|e| TeaQLToolError::ExecutionError(format!("Failed to copy file: {}", e)))
76    }
77
78    /// Rename or move a file/directory
79    pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(&self, from: P, to: Q) -> Result<()> {
80        fs::rename(from.as_ref(), to.as_ref())
81            .map_err(|e| TeaQLToolError::ExecutionError(format!("Failed to rename/move: {}", e)))
82    }
83
84    /// List only files in a directory (non-recursive)
85    pub fn list_files<P: AsRef<Path>>(&self, dir: P) -> Result<Vec<PathBuf>> {
86        self.list_entries(dir, true, false)
87    }
88
89    /// List only directories in a directory (non-recursive)
90    pub fn list_dirs<P: AsRef<Path>>(&self, dir: P) -> Result<Vec<PathBuf>> {
91        self.list_entries(dir, false, true)
92    }
93
94    /// Internal helper to list entries
95    fn list_entries<P: AsRef<Path>>(&self, dir: P, include_files: bool, include_dirs: bool) -> Result<Vec<PathBuf>> {
96        let mut result = Vec::new();
97        let entries = fs::read_dir(dir.as_ref())
98            .map_err(|e| TeaQLToolError::ExecutionError(format!("Failed to read directory: {}", e)))?;
99            
100        for entry in entries {
101            let entry = entry.map_err(|e| TeaQLToolError::ExecutionError(format!("Error reading directory entry: {}", e)))?;
102            let path = entry.path();
103            if path.is_file() && include_files {
104                result.push(path);
105            } else if path.is_dir() && include_dirs {
106                result.push(path);
107            }
108        }
109        Ok(result)
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116    use std::env;
117
118    #[test]
119    fn test_file_operations() {
120        let tool = FileTool;
121        let mut tmp_path = env::temp_dir();
122        tmp_path.push("teaql_test_file.txt");
123        
124        // Write
125        tool.write_string(&tmp_path, "hello teaql").unwrap();
126        assert!(tool.exists(&tmp_path));
127        assert!(tool.is_file(&tmp_path));
128        
129        // Read
130        let content = tool.read_string(&tmp_path).unwrap();
131        assert_eq!(content, "hello teaql");
132        
133        // Delete
134        tool.delete_file(&tmp_path).unwrap();
135        assert!(!tool.exists(&tmp_path));
136    }
137}