aether/builtins/
filesystem.rs1use crate::evaluator::RuntimeError;
5use crate::sandbox::get_filesystem_validator;
6use crate::value::Value;
7use std::fs;
8use std::path::Path;
9
10fn get_string(val: &Value) -> Result<String, RuntimeError> {
12 match val {
13 Value::String(s) => Ok(s.clone()),
14 _ => Err(RuntimeError::TypeErrorDetailed {
15 expected: "String".to_string(),
16 got: format!("{:?}", val),
17 }),
18 }
19}
20
21fn validate_path(path_str: &str) -> Result<std::path::PathBuf, RuntimeError> {
23 if let Some(validator) = get_filesystem_validator() {
25 let path = Path::new(path_str);
26 validator
27 .validate_and_normalize(path)
28 .map_err(|e| RuntimeError::CustomError(format!("Path validation failed: {}", e)))
29 } else {
30 Ok(std::path::PathBuf::from(path_str))
32 }
33}
34
35pub fn read_file(args: &[Value]) -> Result<Value, RuntimeError> {
46 if args.is_empty() {
47 return Err(RuntimeError::WrongArity {
48 expected: 1,
49 got: 0,
50 });
51 }
52
53 let path_str = get_string(&args[0])?;
54
55 let validated_path = validate_path(&path_str)?;
57
58 match fs::read_to_string(&validated_path) {
59 Ok(content) => Ok(Value::String(content)),
60 Err(e) => Err(RuntimeError::CustomError(format!(
61 "Failed to read file '{}': {}",
62 validated_path.display(),
63 e
64 ))),
65 }
66}
67
68pub fn write_file(args: &[Value]) -> Result<Value, RuntimeError> {
80 if args.len() < 2 {
81 return Err(RuntimeError::WrongArity {
82 expected: 2,
83 got: args.len(),
84 });
85 }
86
87 let path_str = get_string(&args[0])?;
88 let content = get_string(&args[1])?;
89
90 let validated_path = validate_path(&path_str)?;
92
93 match fs::write(&validated_path, content) {
94 Ok(_) => Ok(Value::Boolean(true)),
95 Err(e) => Err(RuntimeError::CustomError(format!(
96 "Failed to write file '{}': {}",
97 validated_path.display(),
98 e
99 ))),
100 }
101}
102
103pub fn append_file(args: &[Value]) -> Result<Value, RuntimeError> {
115 if args.len() < 2 {
116 return Err(RuntimeError::WrongArity {
117 expected: 2,
118 got: args.len(),
119 });
120 }
121
122 let path_str = get_string(&args[0])?;
123 let content = get_string(&args[1])?;
124
125 let validated_path = validate_path(&path_str)?;
127
128 match fs::OpenOptions::new()
129 .create(true)
130 .append(true)
131 .open(&validated_path)
132 {
133 Ok(mut file) => {
134 use std::io::Write;
135 match file.write_all(content.as_bytes()) {
136 Ok(_) => Ok(Value::Boolean(true)),
137 Err(e) => Err(RuntimeError::CustomError(format!(
138 "Failed to append to file '{}': {}",
139 validated_path.display(),
140 e
141 ))),
142 }
143 }
144 Err(e) => Err(RuntimeError::CustomError(format!(
145 "Failed to open file '{}': {}",
146 validated_path.display(),
147 e
148 ))),
149 }
150}
151
152pub fn delete_file(args: &[Value]) -> Result<Value, RuntimeError> {
163 if args.is_empty() {
164 return Err(RuntimeError::WrongArity {
165 expected: 1,
166 got: 0,
167 });
168 }
169
170 let path_str = get_string(&args[0])?;
171
172 let validated_path = validate_path(&path_str)?;
174
175 match fs::remove_file(&validated_path) {
176 Ok(_) => Ok(Value::Boolean(true)),
177 Err(e) => Err(RuntimeError::CustomError(format!(
178 "Failed to delete file '{}': {}",
179 validated_path.display(),
180 e
181 ))),
182 }
183}
184
185pub fn file_exists(args: &[Value]) -> Result<Value, RuntimeError> {
193 if args.is_empty() {
194 return Err(RuntimeError::WrongArity {
195 expected: 1,
196 got: 0,
197 });
198 }
199
200 let path = get_string(&args[0])?;
201 Ok(Value::Boolean(Path::new(&path).exists()))
202}
203
204pub fn list_dir(args: &[Value]) -> Result<Value, RuntimeError> {
215 if args.is_empty() {
216 return Err(RuntimeError::WrongArity {
217 expected: 1,
218 got: 0,
219 });
220 }
221
222 let path_str = get_string(&args[0])?;
223
224 let validated_path = validate_path(&path_str)?;
226
227 match fs::read_dir(&validated_path) {
228 Ok(entries) => {
229 let mut items = Vec::new();
230 for entry in entries {
231 match entry {
232 Ok(e) => {
233 if let Some(name) = e.file_name().to_str() {
234 items.push(Value::String(name.to_string()));
235 }
236 }
237 Err(e) => {
238 return Err(RuntimeError::CustomError(format!(
239 "Failed to read directory entry: {}",
240 e
241 )));
242 }
243 }
244 }
245 Ok(Value::Array(items))
246 }
247 Err(e) => Err(RuntimeError::CustomError(format!(
248 "Failed to list directory '{}': {}",
249 validated_path.display(),
250 e
251 ))),
252 }
253}
254
255pub fn create_dir(args: &[Value]) -> Result<Value, RuntimeError> {
266 if args.is_empty() {
267 return Err(RuntimeError::WrongArity {
268 expected: 1,
269 got: 0,
270 });
271 }
272
273 let path_str = get_string(&args[0])?;
274
275 let validated_path = validate_path(&path_str)?;
277
278 match fs::create_dir_all(&validated_path) {
279 Ok(_) => Ok(Value::Boolean(true)),
280 Err(e) => Err(RuntimeError::CustomError(format!(
281 "Failed to create directory '{}': {}",
282 validated_path.display(),
283 e
284 ))),
285 }
286}