Mountain/IPC/WindServiceHandlers/Utilities/
PathExtraction.rs1#![allow(non_snake_case, unused_variables, dead_code, unused_imports)]
2
3use serde_json::Value;
10
11use super::{ApplicationRoot::get_static_application_root, UserdataDir::get_userdata_base_dir};
12use crate::dev_log;
13
14pub fn extract_path_from_arg(Arg:&Value) -> Result<String, String> {
21 if let Some(Path) = Arg.as_str() {
22 return Ok(normalize_uri_path(Path));
23 }
24 if let Some(Object) = Arg.as_object() {
25 if let Some(FsPath) = Object.get("fsPath").and_then(|V| V.as_str()) {
26 if !FsPath.is_empty() {
27 return Ok(FsPath.to_string());
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 if let Some(External) = Object.get("external").and_then(|V| V.as_str()) {
36 if External.starts_with("file://") {
37 let Stripped = External.trim_start_matches("file://");
38 return Ok(normalize_uri_path(Stripped));
39 }
40 }
41 }
42 Err("File path must be a string or URI object with path/fsPath field".to_string())
43}
44
45fn normalize_uri_path(Path:&str) -> String {
46 let Decoded = percent_decode(Path);
47 let Resolved = resolve_userdata_path(&Decoded);
48 let Resolved = resolve_static_application_path(&Resolved);
49
50 #[cfg(target_os = "windows")]
51 {
52 let Trimmed = if Resolved.len() >= 3 && Resolved.starts_with('/') && Resolved.as_bytes().get(2) == Some(&b':') {
53 Resolved[1..].to_string()
54 } else {
55 Resolved
56 };
57 Trimmed.replace('/', "\\")
58 }
59
60 #[cfg(not(target_os = "windows"))]
61 {
62 Resolved
63 }
64}
65
66fn resolve_userdata_path(Path:&str) -> String {
67 if !Path.starts_with("/User/") && Path != "/User" {
68 return Path.to_string();
69 }
70
71 let UserDataBase = get_userdata_base_dir();
72 let Resolved = format!("{}{}", UserDataBase, Path);
73 dev_log!("vfs", "resolve_userdata: {} -> {}", Path, Resolved);
74 Resolved
75}
76
77fn resolve_static_application_path(Path:&str) -> String {
85 let Normalized = if Path.starts_with("/Static/Application/") || Path == "/Static/Application" {
86 Path.to_string()
87 } else if Path.starts_with("Static/Application/") || Path == "Static/Application" {
88 format!("/{}", Path)
89 } else {
90 return Path.to_string();
91 };
92
93 if let Some(Root) = get_static_application_root() {
94 let Relative = Normalized.strip_prefix("/Static/Application").unwrap_or("");
95 let Resolved = format!("{}/Static/Application{}", Root, Relative);
96 dev_log!("vfs", "resolve_static: {} -> {}", Path, Resolved);
97 Resolved
98 } else {
99 Path.to_string()
100 }
101}
102
103pub fn percent_decode(Input:&str) -> String {
106 let mut Result = String::with_capacity(Input.len());
107 let Bytes = Input.as_bytes();
108 let mut I = 0;
109
110 while I < Bytes.len() {
111 if Bytes[I] == b'%' && I + 2 < Bytes.len() {
112 let High = hex_digit(Bytes[I + 1]);
113 let Low = hex_digit(Bytes[I + 2]);
114 if let (Some(H), Some(L)) = (High, Low) {
115 Result.push((H * 16 + L) as char);
116 I += 3;
117 continue;
118 }
119 }
120 Result.push(Bytes[I] as char);
121 I += 1;
122 }
123
124 Result
125}
126
127pub fn hex_digit(Byte:u8) -> Option<u8> {
128 match Byte {
129 b'0'..=b'9' => Some(Byte - b'0'),
130 b'a'..=b'f' => Some(Byte - b'a' + 10),
131 b'A'..=b'F' => Some(Byte - b'A' + 10),
132 _ => None,
133 }
134}