DevelopmentNodeEnvironment_MicrosoftVSCodeDependency_22NodeVersion_Bundle_Clean_Debug_ElectronProfile_EsbuildCompiler_Mountain/IPC/WindServiceHandlers/Utilities/
PathExtraction.rs1use serde_json::Value;
8
9use super::{ApplicationRoot::Get::Fn as get_static_application_root, UserdataDir::Get::Fn as get_userdata_base_dir};
10use crate::dev_log;
11
12pub fn Fn(Arg:&Value) -> Result<String, String> {
19 if let Some(Path) = Arg.as_str() {
20 return Ok(normalize_uri_path(Path));
21 }
22
23 if let Some(Object) = Arg.as_object() {
24 if let Some(FsPath) = Object.get("fsPath").and_then(|V| V.as_str()) {
25 if !FsPath.is_empty() {
26 return Ok(FsPath.to_string());
27 }
28 }
29
30 if let Some(Path) = Object.get("path").and_then(|V| V.as_str()) {
31 if !Path.is_empty() {
32 return Ok(normalize_uri_path(Path));
33 }
34 }
35
36 if let Some(External) = Object.get("external").and_then(|V| V.as_str()) {
37 if External.starts_with("file://") {
38 let Stripped = External.trim_start_matches("file://");
39
40 return Ok(normalize_uri_path(Stripped));
41 }
42 }
43 }
44
45 Err("File path must be a string or URI object with path/fsPath field".to_string())
46}
47
48fn normalize_uri_path(Path:&str) -> String {
49 let Decoded = percent_decode(Path);
50
51 let Resolved = resolve_userdata_path(&Decoded);
52
53 let Resolved = resolve_static_application_path(&Resolved);
54
55 #[cfg(target_os = "windows")]
56 {
57 let Trimmed = if Resolved.len() >= 3 && Resolved.starts_with('/') && Resolved.as_bytes().get(2) == Some(&b':') {
58 Resolved[1..].to_string()
59 } else {
60 Resolved
61 };
62
63 Trimmed.replace('/', "\\")
64 }
65
66 #[cfg(not(target_os = "windows"))]
67 {
68 Resolved
69 }
70}
71
72fn resolve_userdata_path(Path:&str) -> String {
73 if !Path.starts_with("/User/") && Path != "/User" {
74 return Path.to_string();
75 }
76
77 let UserDataBase = get_userdata_base_dir();
78
79 let Resolved = format!("{}{}", UserDataBase, Path);
80
81 dev_log!("vfs", "resolve_userdata: {} -> {}", Path, Resolved);
82
83 Resolved
84}
85
86fn resolve_static_application_path(Path:&str) -> String {
94 let Normalized = if Path.starts_with("/Static/Application/") || Path == "/Static/Application" {
95 Path.to_string()
96 } else if Path.starts_with("Static/Application/") || Path == "Static/Application" {
97 format!("/{}", Path)
98 } else {
99 return Path.to_string();
100 };
101
102 if let Some(Root) = get_static_application_root() {
103 let Relative = Normalized.strip_prefix("/Static/Application").unwrap_or("");
104
105 let Resolved = format!("{}/Static/Application{}", Root, Relative);
106
107 dev_log!("vfs", "resolve_static: {} -> {}", Path, Resolved);
108
109 Resolved
110 } else {
111 Path.to_string()
112 }
113}
114
115pub fn percent_decode(Input:&str) -> String {
124 let mut DecodedBytes:Vec<u8> = Vec::with_capacity(Input.len());
125
126 let Bytes = Input.as_bytes();
127
128 let mut I = 0;
129
130 while I < Bytes.len() {
131 if Bytes[I] == b'%' && I + 2 < Bytes.len() {
132 let High = hex_digit(Bytes[I + 1]);
133
134 let Low = hex_digit(Bytes[I + 2]);
135
136 if let (Some(H), Some(L)) = (High, Low) {
137 DecodedBytes.push(H * 16 + L);
138
139 I += 3;
140
141 continue;
142 }
143 }
144
145 DecodedBytes.push(Bytes[I]);
146
147 I += 1;
148 }
149
150 String::from_utf8(DecodedBytes).unwrap_or_else(|E| String::from_utf8_lossy(E.as_bytes()).into_owned())
151}
152
153fn hex_digit(Byte:u8) -> Option<u8> {
154 match Byte {
155 b'0'..=b'9' => Some(Byte - b'0'),
156
157 b'a'..=b'f' => Some(Byte - b'a' + 10),
158
159 b'A'..=b'F' => Some(Byte - b'A' + 10),
160
161 _ => None,
162 }
163}