@@ -3,6 +3,8 @@ use serde::{Deserialize, Deserializer, de::Error};
33use std:: borrow:: Cow ;
44
55use std:: path:: { MAIN_SEPARATOR_STR , Path , PathBuf } ;
6+ #[ cfg( windows) ]
7+ use std:: sync:: LazyLock ;
68
79#[ derive( Debug , Default , Clone ) ]
810pub struct Trie < T > {
@@ -32,11 +34,62 @@ impl<T> Trie<T> {
3234 }
3335}
3436
37+ #[ cfg( windows) ]
38+ static WINDOWS_PATH_REGEXP : LazyLock < Regex > =
39+ LazyLock :: new ( || Regex :: new ( r"^([a-zA-Z]:.*)$" ) . unwrap ( ) ) ;
40+ #[ cfg( windows) ]
41+ static UNC_WINDOWS_PATH_REGEXP : LazyLock < Regex > =
42+ LazyLock :: new ( || Regex :: new ( r"^[\/\\][\/\\](\.[\/\\])?(.*)$" ) . unwrap ( ) ) ;
43+ #[ cfg( windows) ]
44+ static PORTABLE_PATH_REGEXP : LazyLock < Regex > =
45+ LazyLock :: new ( || Regex :: new ( r"^\/([a-zA-Z]:.*)$" ) . unwrap ( ) ) ;
46+ #[ cfg( windows) ]
47+ static UNC_PORTABLE_PATH_REGEXP : LazyLock < Regex > =
48+ LazyLock :: new ( || Regex :: new ( r"^\/unc\/(\.dot\/)?(.*)$" ) . unwrap ( ) ) ;
49+
50+ fn from_portable_path < ' a > ( str : & ' a str ) -> Cow < ' a , str > {
51+ #[ cfg( windows) ]
52+ {
53+ if let Ok ( Some ( caps) ) = PORTABLE_PATH_REGEXP . captures ( str) {
54+ return Cow :: Borrowed ( caps. get ( 1 ) . unwrap ( ) . as_str ( ) ) ;
55+ }
56+
57+ if let Ok ( Some ( caps) ) = UNC_PORTABLE_PATH_REGEXP . captures ( str) {
58+ if caps. get ( 1 ) . is_some ( ) {
59+ return Cow :: Owned ( format ! ( "\\ \\ .\\ {}" , caps. get( 2 ) . unwrap( ) . as_str( ) ) ) ;
60+ } else {
61+ return Cow :: Owned ( format ! ( "\\ \\ {}" , caps. get( 2 ) . unwrap( ) . as_str( ) ) ) ;
62+ }
63+ }
64+ }
65+
66+ Cow :: Borrowed ( str)
67+ }
68+
69+ fn to_portable_path < ' a > ( str : & ' a str ) -> Cow < ' a , str > {
70+ #[ cfg( windows) ]
71+ {
72+ if let Ok ( Some ( caps) ) = WINDOWS_PATH_REGEXP . captures ( str) {
73+ return Cow :: Owned ( format ! ( "/{}" , caps. get( 1 ) . unwrap( ) . as_str( ) ) ) ;
74+ }
75+
76+ if let Ok ( Some ( caps) ) = UNC_WINDOWS_PATH_REGEXP . captures ( str) {
77+ if caps. get ( 1 ) . is_some ( ) {
78+ return Cow :: Owned ( format ! ( "/unc/.dot/{}" , caps. get( 2 ) . unwrap( ) . as_str( ) ) ) ;
79+ } else {
80+ return Cow :: Owned ( format ! ( "/unc/{}" , caps. get( 2 ) . unwrap( ) . as_str( ) ) ) ;
81+ }
82+ }
83+ }
84+
85+ Cow :: Borrowed ( str)
86+ }
87+
3588pub fn normalize_path < P : AsRef < str > > ( original : P ) -> String {
36- let original_str = original. as_ref ( ) ;
89+ let original_str = to_portable_path ( original. as_ref ( ) ) ;
3790
3891 let check_str_root = original_str. strip_prefix ( "/" ) ;
39- let str_minus_root = check_str_root. unwrap_or ( original_str) ;
92+ let str_minus_root = check_str_root. unwrap_or ( original_str. as_ref ( ) ) ;
4093
4194 let components = str_minus_root. split ( & [ '/' , '\\' ] [ ..] ) ;
4295
@@ -86,7 +139,7 @@ pub fn normalize_path<P: AsRef<str>>(original: P) -> String {
86139 str. push ( '/' ) ;
87140 }
88141
89- str
142+ from_portable_path ( & str) . into_owned ( )
90143}
91144
92145#[ cfg( test) ]
@@ -106,6 +159,7 @@ mod tests {
106159 assert_eq ! ( normalize_path( "foo/bar/.." ) , "foo" ) ;
107160 assert_eq ! ( normalize_path( "foo/../../bar" ) , "../bar" ) ;
108161 assert_eq ! ( normalize_path( "../foo/../../bar" ) , "../../bar" ) ;
162+ assert_eq ! ( normalize_path( "foo/../../bar" ) , "../bar" ) ;
109163 assert_eq ! ( normalize_path( "./foo" ) , "foo" ) ;
110164 assert_eq ! ( normalize_path( "../foo" ) , "../foo" ) ;
111165 assert_eq ! ( normalize_path( "../D:/foo" ) , "../D:/foo" ) ;
@@ -114,6 +168,18 @@ mod tests {
114168 assert_eq ! ( normalize_path( "/../foo/bar" ) , "/foo/bar" ) ;
115169 assert_eq ! ( normalize_path( "/../foo/bar//" ) , "/foo/bar/" ) ;
116170 assert_eq ! ( normalize_path( "/foo/bar/" ) , "/foo/bar/" ) ;
171+
172+ #[ cfg( windows) ]
173+ assert_eq ! ( normalize_path( "D:\\ foo\\ ..\\ bar" ) , "D:/bar" ) ;
174+ #[ cfg( windows) ]
175+ assert_eq ! ( normalize_path( "D:\\ foo\\ ..\\ ..\\ C:\\ bar\\ test" ) , "C:/bar/test" ) ;
176+ #[ cfg( windows) ]
177+ assert_eq ! ( normalize_path( "\\ \\ server-name\\ foo\\ ..\\ bar" ) , "\\ \\ server-name/bar" ) ;
178+ #[ cfg( windows) ]
179+ assert_eq ! (
180+ normalize_path( "\\ \\ server-name\\ foo\\ ..\\ ..\\ ..\\ C:\\ bar\\ test" ) ,
181+ "C:/bar/test"
182+ ) ;
117183 }
118184}
119185
0 commit comments