lean_ctx/core/
pathutil.rs1use std::path::{Path, PathBuf};
2
3pub fn safe_canonicalize(path: &Path) -> std::io::Result<PathBuf> {
9 let canon = std::fs::canonicalize(path)?;
10 Ok(strip_verbatim(canon))
11}
12
13pub fn safe_canonicalize_or_self(path: &Path) -> PathBuf {
15 safe_canonicalize(path).unwrap_or_else(|_| path.to_path_buf())
16}
17
18pub fn strip_verbatim(path: PathBuf) -> PathBuf {
21 let s = path.to_string_lossy();
22 if let Some(stripped) = strip_verbatim_str(&s) {
23 PathBuf::from(stripped)
24 } else {
25 path
26 }
27}
28
29pub fn strip_verbatim_str(path: &str) -> Option<String> {
32 let normalized = path.replace('\\', "/");
33
34 if let Some(rest) = normalized.strip_prefix("//?/UNC/") {
35 Some(format!("//{rest}"))
36 } else {
37 normalized
38 .strip_prefix("//?/")
39 .map(std::string::ToString::to_string)
40 }
41}
42
43#[cfg(test)]
44mod tests {
45 use super::*;
46
47 #[test]
48 fn strip_regular_verbatim() {
49 let p = PathBuf::from(r"\\?\C:\Users\dev\project");
50 let result = strip_verbatim(p);
51 assert_eq!(result, PathBuf::from("C:/Users/dev/project"));
52 }
53
54 #[test]
55 fn strip_unc_verbatim() {
56 let p = PathBuf::from(r"\\?\UNC\server\share\dir");
57 let result = strip_verbatim(p);
58 assert_eq!(result, PathBuf::from("//server/share/dir"));
59 }
60
61 #[test]
62 fn no_prefix_unchanged() {
63 let p = PathBuf::from("/home/user/project");
64 let result = strip_verbatim(p.clone());
65 assert_eq!(result, p);
66 }
67
68 #[test]
69 fn windows_drive_unchanged() {
70 let p = PathBuf::from("C:/Users/dev");
71 let result = strip_verbatim(p.clone());
72 assert_eq!(result, p);
73 }
74
75 #[test]
76 fn strip_str_regular() {
77 assert_eq!(
78 strip_verbatim_str(r"\\?\E:\code\lean-ctx"),
79 Some("E:/code/lean-ctx".to_string())
80 );
81 }
82
83 #[test]
84 fn strip_str_unc() {
85 assert_eq!(
86 strip_verbatim_str(r"\\?\UNC\myserver\data"),
87 Some("//myserver/data".to_string())
88 );
89 }
90
91 #[test]
92 fn strip_str_forward_slash_variant() {
93 assert_eq!(
94 strip_verbatim_str("//?/C:/Users/dev"),
95 Some("C:/Users/dev".to_string())
96 );
97 }
98
99 #[test]
100 fn strip_str_no_prefix() {
101 assert_eq!(strip_verbatim_str("/home/user"), None);
102 }
103
104 #[test]
105 fn safe_canonicalize_or_self_nonexistent() {
106 let p = Path::new("/this/path/should/not/exist/xyzzy");
107 let result = safe_canonicalize_or_self(p);
108 assert_eq!(result, p.to_path_buf());
109 }
110}