miden_debug_engine/
source_path.rs1use alloc::{borrow::ToOwned, string::String, vec::Vec};
2
3use miden_debug_types::Uri;
4
5pub fn normalize_source_path(path: &str) -> String {
7 let path = path.trim();
8 let path = Uri::new(path)
9 .to_path()
10 .map(|path| path.to_string_lossy().into_owned())
11 .unwrap_or_else(|| path.to_owned());
12 let mut path = path.replace('\\', "/");
13 if path
14 .as_bytes()
15 .get(0..2)
16 .is_some_and(|bytes| bytes[0].is_ascii_alphabetic() && bytes[1] == b':')
17 {
18 path.replace_range(..1, &path[..1].to_ascii_lowercase());
19 }
20
21 let is_absolute = path.starts_with('/');
22 let mut parts = Vec::new();
23 for part in path.split('/') {
24 match part {
25 "" | "." => {}
26 ".." => {
27 if parts.last().is_some_and(|last| *last != "..") {
28 parts.pop();
29 } else {
30 parts.push(part);
31 }
32 }
33 _ => parts.push(part),
34 }
35 }
36
37 let normalized = parts.join("/");
38 if is_absolute && !normalized.is_empty() {
39 format!("/{normalized}")
40 } else {
41 normalized
42 }
43}
44
45#[cfg(test)]
46mod tests {
47 use super::normalize_source_path;
48
49 #[test]
50 fn normalizes_file_uris_and_windows_drive_letters() {
51 assert_eq!(
52 normalize_source_path("file:///C:/Users/me/program.masm"),
53 "c:/Users/me/program.masm"
54 );
55 assert_eq!(
56 normalize_source_path("file:///c:/Users/me/program.masm"),
57 "c:/Users/me/program.masm"
58 );
59 assert_eq!(
60 normalize_source_path("file://localhost/C:/Users/me/program.masm"),
61 "c:/Users/me/program.masm"
62 );
63 assert_eq!(
64 normalize_source_path("C:\\Users\\me\\program.masm"),
65 "c:/Users/me/program.masm"
66 );
67 }
68
69 #[test]
70 fn normalizes_path_components() {
71 assert_eq!(
72 normalize_source_path("/home/me/./src/../program.masm"),
73 "/home/me/program.masm"
74 );
75 assert_eq!(
76 normalize_source_path("relative/./src/../program.masm"),
77 "relative/program.masm"
78 );
79 }
80}