Mountain/IPC/WindServiceHandlers/FileSystem/Native/
FileWriteNative.rs1use serde_json::{Value, json};
8
9use crate::{IPC::WindServiceHandlers::Utilities::PathExtraction::Fn as extract_path_from_arg, dev_log};
10
11pub async fn Fn(Arguments:Vec<Value>) -> Result<Value, String> {
12 let ResourceArg = Arguments.get(0).ok_or("Missing file path")?;
13
14 let ExternalUri = ResourceArg
17 .as_object()
18 .and_then(|O| O.get("external"))
19 .and_then(|V| V.as_str())
20 .map(|S| S.to_string());
21
22 let Path = extract_path_from_arg(ResourceArg)?;
23
24 let Content = Arguments.get(1).ok_or("Missing file content")?;
25
26 let Bytes = if let Some(S) = Content.as_str() {
27 S.as_bytes().to_vec()
28 } else if let Some(Obj) = Content.as_object() {
29 if let Some(Buf) = Obj.get("buffer") {
30 if let Some(Arr) = Buf.as_array() {
31 Arr.iter().filter_map(|V| V.as_u64().map(|N| N as u8)).collect()
32 } else if let Some(S) = Buf.as_str() {
33 use base64::Engine as _;
36
37 base64::engine::general_purpose::STANDARD
38 .decode(S)
39 .unwrap_or_else(|_| S.as_bytes().to_vec())
40 } else {
41 return Err("Unsupported buffer format".to_string());
42 }
43 } else {
44 serde_json::to_string(Content).unwrap_or_default().into_bytes()
45 }
46 } else {
47 return Err("File content must be a string or VSBuffer".to_string());
48 };
49
50 if let Some(Parent) = std::path::Path::new(&Path).parent() {
51 tokio::fs::create_dir_all(Parent).await.ok();
52 }
53
54 let Start = std::time::Instant::now();
55
56 tokio::fs::write(&Path, &Bytes)
57 .await
58 .map_err(|E| format!("Failed to write file: {} (path: {})", E, Path))?;
59
60 let ElapsedMs = Start.elapsed().as_millis();
61
62 dev_log!("vfs", "file:write ok path={} bytes={} ms={}", Path, Bytes.len(), ElapsedMs);
63
64 let FileUri = ExternalUri.unwrap_or_else(|| format!("file://{}", Path));
68
69 tokio::spawn(async move {
70 if let Err(Error) = ::Vine::Client::SendNotification::Fn(
71 "cocoon-main".to_string(),
72 "$acceptModelSaved".to_string(),
73 json!({ "uri": FileUri }),
74 )
75 .await
76 {
77 let ErrStr = format!("{:?}", Error);
78
79 if ErrStr.contains("ClientNotConnected") {
80 dev_log!(
81 "vfs-verbose",
82 "[FileWriteNative] $acceptModelSaved skipped (Cocoon not yet connected)"
83 );
84 } else {
85 dev_log!("vfs", "warn: [FileWriteNative] $acceptModelSaved notify failed: {:?}", Error);
86 }
87 }
88 });
89
90 match tokio::fs::metadata(&Path).await {
94 Ok(Meta) => Ok(crate::IPC::WindServiceHandlers::Utilities::MetadataEncoding::Fn(&Meta)),
95
96 Err(E) => Err(format!("file:write post-stat failed for {}: {}", Path, E)),
101 }
102}