Mountain/IPC/WindServiceHandler/
FileSystem.rs1#![allow(non_snake_case)]
2
3use std::{path::PathBuf, sync::Arc};
9
10use serde_json::{Value, json};
11use CommonLibrary::{
12 Environment::Requires::Requires,
13 Error::CommonError::CommonError,
14 FileSystem::{FileSystemReader::FileSystemReader, FileSystemWriter::FileSystemWriter},
15};
16
17use crate::{RunTime::ApplicationRunTime::ApplicationRunTime, dev_log};
18use super::{extract_path_from_arg, metadata_to_istat};
19
20pub async fn handle_file_read(Runtime:Arc<ApplicationRunTime>, Args:Vec<Value>) -> Result<Value, String> {
26 let Path = Args
27 .get(0)
28 .ok_or("Missing file path".to_string())?
29 .as_str()
30 .ok_or("File path must be a string".to_string())?;
31
32 let Provider:Arc<dyn FileSystemReader> = Runtime.Environment.Require();
33
34 let Content = Provider
35 .ReadFile(&PathBuf::from(Path))
36 .await
37 .map_err(|E| format!("Failed to read file: {}", E))?;
38
39 dev_log!("vfs", "read: {} ({} bytes)", Path, Content.len());
40 Ok(json!(Content))
41}
42
43pub async fn handle_file_write(Runtime:Arc<ApplicationRunTime>, Args:Vec<Value>) -> Result<Value, String> {
45 let Path = Args
46 .get(0)
47 .ok_or("Missing file path".to_string())?
48 .as_str()
49 .ok_or("File path must be a string".to_string())?;
50
51 let Content = Args
52 .get(1)
53 .ok_or("Missing file content".to_string())?
54 .as_str()
55 .ok_or("File content must be a string".to_string())?;
56
57 let Provider:Arc<dyn FileSystemWriter> = Runtime.Environment.Require();
58
59 Provider
60 .WriteFile(&PathBuf::from(Path), Content.as_bytes().to_vec(), true, true)
61 .await
62 .map_err(|E:CommonError| format!("Failed to write file: {}", E))?;
63
64 dev_log!("vfs", "written: {} ({} bytes)", Path, Content.len());
65 Ok(Value::Null)
66}
67
68pub async fn handle_file_stat(Runtime:Arc<ApplicationRunTime>, Args:Vec<Value>) -> Result<Value, String> {
70 let Path = Args
71 .get(0)
72 .ok_or("Missing file path".to_string())?
73 .as_str()
74 .ok_or("File path must be a string".to_string())?;
75
76 let Provider:Arc<dyn FileSystemReader> = Runtime.Environment.Require();
77
78 let Stats = Provider
79 .StatFile(&PathBuf::from(Path))
80 .await
81 .map_err(|E| format!("Failed to stat file: {}", E))?;
82
83 dev_log!("vfs", "legacy_stat: {}", Path);
84 Ok(json!(Stats))
85}
86
87pub async fn handle_file_exists(Runtime:Arc<ApplicationRunTime>, Args:Vec<Value>) -> Result<Value, String> {
89 let Path = Args
90 .get(0)
91 .ok_or("Missing file path".to_string())?
92 .as_str()
93 .ok_or("File path must be a string".to_string())?;
94
95 let Provider:Arc<dyn FileSystemReader> = Runtime.Environment.Require();
96
97 let Exists = Provider.StatFile(&PathBuf::from(Path)).await.is_ok();
98
99 dev_log!("vfs", "exists: {} = {}", Path, Exists);
100 Ok(json!(Exists))
101}
102
103pub async fn handle_file_delete(Runtime:Arc<ApplicationRunTime>, Args:Vec<Value>) -> Result<Value, String> {
105 let Path = Args
106 .get(0)
107 .ok_or("Missing file path".to_string())?
108 .as_str()
109 .ok_or("File path must be a string".to_string())?;
110
111 let Provider:Arc<dyn FileSystemWriter> = Runtime.Environment.Require();
112
113 Provider
114 .Delete(&PathBuf::from(Path), false, false)
115 .await
116 .map_err(|E:CommonError| format!("Failed to delete file: {}", E))?;
117
118 dev_log!("vfs", "deleted: {}", Path);
119 Ok(Value::Null)
120}
121
122pub async fn handle_file_copy(Runtime:Arc<ApplicationRunTime>, Args:Vec<Value>) -> Result<Value, String> {
124 let Source = Args
125 .get(0)
126 .ok_or("Missing source path".to_string())?
127 .as_str()
128 .ok_or("Source path must be a string".to_string())?;
129
130 let Destination = Args
131 .get(1)
132 .ok_or("Missing destination path".to_string())?
133 .as_str()
134 .ok_or("Destination path must be a string".to_string())?;
135
136 let Provider:Arc<dyn FileSystemWriter> = Runtime.Environment.Require();
137
138 Provider
139 .Copy(&PathBuf::from(Source), &PathBuf::from(Destination), false)
140 .await
141 .map_err(|_E:CommonError| format!("Failed to copy file: {} -> {}", Source, Destination))?;
142
143 dev_log!("vfs", "copied: {} -> {}", Source, Destination);
144 Ok(Value::Null)
145}
146
147pub async fn handle_file_move(Runtime:Arc<ApplicationRunTime>, Args:Vec<Value>) -> Result<Value, String> {
149 let Source = Args
150 .get(0)
151 .ok_or("Missing source path".to_string())?
152 .as_str()
153 .ok_or("Source path must be a string".to_string())?;
154
155 let Destination = Args
156 .get(1)
157 .ok_or("Missing destination path".to_string())?
158 .as_str()
159 .ok_or("Destination path must be a string".to_string())?;
160
161 let Provider:Arc<dyn FileSystemWriter> = Runtime.Environment.Require();
162
163 Provider
164 .Rename(&PathBuf::from(Source), &PathBuf::from(Destination), false)
165 .await
166 .map_err(|_E:CommonError| format!("Failed to move file: {} -> {}", Source, Destination))?;
167
168 dev_log!("vfs", "moved: {} -> {}", Source, Destination);
169 Ok(Value::Null)
170}
171
172pub async fn handle_file_mkdir(Runtime:Arc<ApplicationRunTime>, Args:Vec<Value>) -> Result<Value, String> {
174 let Path = Args
175 .get(0)
176 .ok_or("Missing directory path".to_string())?
177 .as_str()
178 .ok_or("Directory path must be a string".to_string())?;
179
180 let Recursive = Args.get(1).and_then(|V| V.as_bool()).unwrap_or(true);
181
182 let Provider:Arc<dyn FileSystemWriter> = Runtime.Environment.Require();
183
184 Provider
185 .CreateDirectory(&PathBuf::from(Path), Recursive)
186 .await
187 .map_err(|E:CommonError| format!("Failed to create directory: {}", E))?;
188
189 dev_log!("vfs", "mkdir: {} (recursive: {})", Path, Recursive);
190 Ok(Value::Null)
191}
192
193pub async fn handle_file_readdir(Runtime:Arc<ApplicationRunTime>, Args:Vec<Value>) -> Result<Value, String> {
195 let Path = Args
196 .get(0)
197 .ok_or("Missing directory path".to_string())?
198 .as_str()
199 .ok_or("Directory path must be a string".to_string())?;
200
201 let Provider:Arc<dyn FileSystemReader> = Runtime.Environment.Require();
202
203 let Entries = Provider
204 .ReadDirectory(&PathBuf::from(Path))
205 .await
206 .map_err(|E| format!("Failed to read directory: {}", E))?;
207
208 dev_log!("vfs", "readdir_legacy: {} ({} entries)", Path, Entries.len());
209 Ok(json!(Entries))
210}
211
212pub async fn handle_file_read_binary(Runtime:Arc<ApplicationRunTime>, Args:Vec<Value>) -> Result<Value, String> {
214 let Path = Args
215 .get(0)
216 .ok_or("Missing file path".to_string())?
217 .as_str()
218 .ok_or("File path must be a string".to_string())?;
219
220 let Provider:Arc<dyn FileSystemReader> = Runtime.Environment.Require();
221
222 let Content = Provider
223 .ReadFile(&PathBuf::from(Path))
224 .await
225 .map_err(|E| format!("Failed to read binary file: {}", E))?;
226
227 dev_log!("vfs", "readBinary: {} ({} bytes)", Path, Content.len());
228 Ok(json!(Content))
229}
230
231pub async fn handle_file_write_binary(Runtime:Arc<ApplicationRunTime>, Args:Vec<Value>) -> Result<Value, String> {
233 let Path = Args
234 .get(0)
235 .ok_or("Missing file path".to_string())?
236 .as_str()
237 .ok_or("File path must be a string".to_string())?;
238
239 let Content = Args
240 .get(1)
241 .ok_or("Missing file content".to_string())?
242 .as_str()
243 .ok_or("File content must be a string".to_string())?;
244
245 let ContentBytes = Content.as_bytes().to_vec();
246 let ContentLength = ContentBytes.len();
247
248 let Provider:Arc<dyn FileSystemWriter> = Runtime.Environment.Require();
249
250 Provider
251 .WriteFile(&PathBuf::from(Path), ContentBytes.clone(), true, true)
252 .await
253 .map_err(|E:CommonError| format!("Failed to write binary file: {}", E))?;
254
255 dev_log!("vfs", "writeBinary: {} ({} bytes)", Path, ContentLength);
256 Ok(Value::Null)
257}
258
259pub async fn handle_file_read_native(Args:Vec<Value>) -> Result<Value, String> {
266 let Path = extract_path_from_arg(Args.get(0).ok_or("Missing file path")?)?;
267
268 dev_log!("vfs", "readFile: {}", Path);
269
270 let Bytes = tokio::fs::read(&Path)
271 .await
272 .map_err(|E| format!("Failed to read file: {} (path: {})", E, Path))?;
273
274 dev_log!("vfs", "readFile OK: {} ({} bytes)", Path, Bytes.len());
275
276 let ByteArray:Vec<Value> = Bytes.iter().map(|B| json!(*B)).collect();
277 Ok(json!({ "buffer": ByteArray }))
278}
279
280pub async fn handle_file_write_native(Args:Vec<Value>) -> Result<Value, String> {
282 let Path = extract_path_from_arg(Args.get(0).ok_or("Missing file path")?)?;
283
284 let Content = Args.get(1).ok_or("Missing file content")?;
285
286 let Bytes = if let Some(S) = Content.as_str() {
287 S.as_bytes().to_vec()
288 } else if let Some(Obj) = Content.as_object() {
289 if let Some(Buf) = Obj.get("buffer") {
290 if let Some(Arr) = Buf.as_array() {
291 Arr.iter().filter_map(|V| V.as_u64().map(|N| N as u8)).collect()
292 } else if let Some(S) = Buf.as_str() {
293 S.as_bytes().to_vec()
294 } else {
295 return Err("Unsupported buffer format".to_string());
296 }
297 } else {
298 serde_json::to_string(Content).unwrap_or_default().into_bytes()
299 }
300 } else {
301 return Err("File content must be a string or VSBuffer".to_string());
302 };
303
304 if let Some(Parent) = std::path::Path::new(&Path).parent() {
305 tokio::fs::create_dir_all(Parent).await.ok();
306 }
307
308 tokio::fs::write(&Path, &Bytes)
309 .await
310 .map_err(|E| format!("Failed to write file: {} (path: {})", E, Path))?;
311
312 Ok(Value::Null)
313}
314
315pub async fn handle_file_stat_native(Args:Vec<Value>) -> Result<Value, String> {
317 let Path = extract_path_from_arg(Args.get(0).ok_or("Missing file path")?)?;
318
319 dev_log!("vfs", "stat: {}", Path);
320
321 let Metadata = tokio::fs::symlink_metadata(&Path).await.map_err(|E| {
322 dev_log!("vfs", "stat ENOENT: {}", Path);
323 format!("Failed to stat file: {} (path: {})", E, Path)
324 })?;
325
326 dev_log!("vfs", "stat OK: {} (dir={})", Path, Metadata.is_dir());
327 Ok(metadata_to_istat(&Metadata))
328}
329
330pub async fn handle_file_exists_native(Args:Vec<Value>) -> Result<Value, String> {
332 let Path = extract_path_from_arg(Args.get(0).ok_or("Missing file path")?)?;
333
334 Ok(json!(tokio::fs::try_exists(&Path).await.unwrap_or(false)))
335}
336
337pub async fn handle_file_delete_native(Args:Vec<Value>) -> Result<Value, String> {
339 let Path = extract_path_from_arg(Args.get(0).ok_or("Missing file path")?)?;
340
341 let Recursive = Args
342 .get(1)
343 .and_then(|V| V.as_object())
344 .and_then(|O| O.get("recursive"))
345 .and_then(|V| V.as_bool())
346 .unwrap_or(false);
347
348 let PathBuf = std::path::Path::new(&Path);
349
350 if PathBuf.is_dir() {
351 if Recursive {
352 tokio::fs::remove_dir_all(&Path).await
353 } else {
354 tokio::fs::remove_dir(&Path).await
355 }
356 } else {
357 tokio::fs::remove_file(&Path).await
358 }
359 .map_err(|E| format!("Failed to delete: {} ({})", Path, E))?;
360
361 Ok(Value::Null)
362}
363
364pub async fn handle_file_mkdir_native(Args:Vec<Value>) -> Result<Value, String> {
366 let Path = extract_path_from_arg(Args.get(0).ok_or("Missing directory path")?)?;
367
368 tokio::fs::create_dir_all(&Path)
369 .await
370 .map_err(|E| format!("Failed to mkdir: {} ({})", Path, E))?;
371
372 Ok(Value::Null)
373}
374
375pub async fn handle_file_readdir_native(Args:Vec<Value>) -> Result<Value, String> {
378 let Path = extract_path_from_arg(Args.get(0).ok_or("Missing directory path")?)?;
379
380 dev_log!("vfs", "readdir: {}", Path);
381
382 let mut Entries = tokio::fs::read_dir(&Path)
383 .await
384 .map_err(|E| format!("Failed to readdir: {} ({})", Path, E))?;
385
386 let mut Result = Vec::new();
387
388 while let Some(Entry) = Entries.next_entry().await.map_err(|E| E.to_string())? {
389 let Name = Entry.file_name().to_string_lossy().to_string();
390 let FileType = Entry.file_type().await.map_err(|E| E.to_string())?;
391
392 let TypeValue = if FileType.is_symlink() {
393 64 } else if FileType.is_dir() {
395 2 } else {
397 1 };
399
400 Result.push(json!([Name, TypeValue]));
401 }
402
403 Ok(json!(Result))
404}
405
406pub async fn handle_file_rename_native(Args:Vec<Value>) -> Result<Value, String> {
408 let Source = extract_path_from_arg(Args.get(0).ok_or("Missing source path")?)?;
409 let Target = extract_path_from_arg(Args.get(1).ok_or("Missing target path")?)?;
410
411 tokio::fs::rename(&Source, &Target)
412 .await
413 .map_err(|E| format!("Failed to rename: {} -> {} ({})", Source, Target, E))?;
414
415 Ok(Value::Null)
416}
417
418pub async fn handle_file_realpath(Args:Vec<Value>) -> Result<Value, String> {
420 let Path = extract_path_from_arg(Args.get(0).ok_or("Missing path")?)?;
421
422 let Canonical = tokio::fs::canonicalize(&Path)
423 .await
424 .map_err(|E| format!("Failed to realpath: {} ({})", Path, E))?;
425
426 Ok(json!({
427 "scheme": "file",
428 "path": Canonical.to_string_lossy(),
429 "authority": ""
430 }))
431}
432
433pub async fn handle_file_clone_native(Args:Vec<Value>) -> Result<Value, String> {
435 let Source = extract_path_from_arg(Args.get(0).ok_or("Missing source path")?)?;
436 let Target = extract_path_from_arg(Args.get(1).ok_or("Missing target path")?)?;
437
438 tokio::fs::copy(&Source, &Target)
439 .await
440 .map_err(|E| format!("Failed to clone: {} -> {} ({})", Source, Target, E))?;
441
442 Ok(Value::Null)
443}