@@ -1009,26 +1009,29 @@ impl Glob {
10091009
10101010 /// Format a path according to options (posix, etc.)
10111011 ///
1012+ /// Always converts backslashes to forward slashes for consistent output.
10121013 /// When posix: true on Windows, absolute paths are converted to UNC form
10131014 /// (e.g., `C:\foo\bar` → `//?/C:/foo/bar`) to match glob's behavior.
10141015 fn format_path ( & self , path : & std:: path:: Path ) -> String {
10151016 let path_str = path. to_string_lossy ( ) . to_string ( ) ;
1017+
1018+ // On Windows with posix: true, convert absolute paths to UNC form
1019+ #[ cfg( target_os = "windows" ) ]
10161020 if self . posix {
1017- // On Windows with posix: true, convert absolute paths to UNC form
1018- #[ cfg( target_os = "windows" ) ]
1021+ let bytes = path_str. as_bytes ( ) ;
1022+ if bytes. len ( ) >= 2
1023+ && bytes[ 0 ] . is_ascii_alphabetic ( )
1024+ && ( bytes[ 1 ] == b':' || bytes[ 1 ] == b'\\' || bytes[ 1 ] == b'/' )
10191025 {
1020- let bytes = path_str. as_bytes ( ) ;
1021- if bytes. len ( ) >= 2
1022- && bytes[ 0 ] . is_ascii_alphabetic ( )
1023- && ( bytes[ 1 ] == b':' || bytes[ 1 ] == b'\\' || bytes[ 1 ] == b'/' )
1024- {
1025- // Convert to UNC form: //?/C:/...
1026- let drive = bytes[ 0 ] as char ;
1027- let rest = path_str[ 2 ..] . replace ( '\\' , "/" ) ;
1028- return format ! ( "//?/{drive}:{rest}" ) ;
1029- }
1026+ // Convert to UNC form: //?/C:/...
1027+ let drive = bytes[ 0 ] as char ;
1028+ let rest = path_str[ 2 ..] . replace ( '\\' , "/" ) ;
1029+ return format ! ( "//?/{drive}:{rest}" ) ;
10301030 }
1031- // Standard POSIX conversion: backslashes to forward slashes
1031+ }
1032+
1033+ // Always convert backslashes to forward slashes for consistent output
1034+ if path_str. contains ( '\\' ) {
10321035 path_str. replace ( '\\' , "/" )
10331036 } else {
10341037 path_str
@@ -1039,11 +1042,8 @@ impl Glob {
10391042 fn ensure_trailing_slash ( & self , path : & str ) -> String {
10401043 if path. ends_with ( '/' ) || path. ends_with ( '\\' ) {
10411044 path. to_string ( )
1042- } else if self . should_normalize_backslashes ( ) {
1043- format ! ( "{path}/" )
10441045 } else {
1045- // On Windows without posix option, use the native separator
1046- format ! ( "{path}\\ " )
1046+ format ! ( "{path}/" )
10471047 }
10481048 }
10491049
@@ -1104,37 +1104,38 @@ impl Glob {
11041104 /// Format a path into the provided buffer, returning a reference to the result.
11051105 /// This avoids allocations by reusing the buffer across iterations.
11061106 ///
1107+ /// Always converts backslashes to forward slashes for consistent output.
11071108 /// When posix: true on Windows, absolute paths are converted to UNC form
11081109 /// (e.g., `C:\foo\bar` → `//?/C:/foo/bar`) to match glob's behavior.
11091110 #[ inline]
11101111 fn format_path_into_buffer < ' a > ( & self , path : & Path , buffer : & ' a mut String ) -> & ' a str {
11111112 buffer. clear ( ) ;
11121113 let path_str = path. to_string_lossy ( ) ;
11131114
1115+ // On Windows with posix: true, convert absolute paths to UNC form
1116+ // e.g., C:\foo\bar → //?/C:/foo/bar
1117+ #[ cfg( target_os = "windows" ) ]
11141118 if self . posix {
1115- // On Windows with posix: true, convert absolute paths to UNC form
1116- // e.g., C:\foo\bar → //?/C:/foo/bar
1117- #[ cfg( target_os = "windows" ) ]
1119+ // Check if this is a Windows absolute path (starts with drive letter)
1120+ let bytes = path_str. as_bytes ( ) ;
1121+ if bytes. len ( ) >= 2
1122+ && bytes[ 0 ] . is_ascii_alphabetic ( )
1123+ && ( bytes[ 1 ] == b':' || bytes[ 1 ] == b'\\' || bytes[ 1 ] == b'/' )
11181124 {
1119- // Check if this is a Windows absolute path (starts with drive letter)
1120- let bytes = path_str. as_bytes ( ) ;
1121- if bytes. len ( ) >= 2
1122- && bytes[ 0 ] . is_ascii_alphabetic ( )
1123- && ( bytes[ 1 ] == b':' || bytes[ 1 ] == b'\\' || bytes[ 1 ] == b'/' )
1124- {
1125- // Convert to UNC form: //?/C:/...
1126- buffer. push_str ( "//?/" ) ;
1127- buffer. push ( bytes[ 0 ] as char ) ;
1128- buffer. push ( ':' ) ;
1129- // Skip the drive letter and colon, convert rest with forward slashes
1130- for c in path_str[ 2 ..] . chars ( ) {
1131- buffer. push ( if c == '\\' { '/' } else { c } ) ;
1132- }
1133- return buffer. as_str ( ) ;
1125+ // Convert to UNC form: //?/C:/...
1126+ buffer. push_str ( "//?/" ) ;
1127+ buffer. push ( bytes[ 0 ] as char ) ;
1128+ buffer. push ( ':' ) ;
1129+ // Skip the drive letter and colon, convert rest with forward slashes
1130+ for c in path_str[ 2 ..] . chars ( ) {
1131+ buffer. push ( if c == '\\' { '/' } else { c } ) ;
11341132 }
1133+ return buffer. as_str ( ) ;
11351134 }
1135+ }
11361136
1137- // Standard POSIX conversion: backslashes to forward slashes
1137+ // Always convert backslashes to forward slashes for consistent output
1138+ if path_str. contains ( '\\' ) {
11381139 for c in path_str. chars ( ) {
11391140 buffer. push ( if c == '\\' { '/' } else { c } ) ;
11401141 }
@@ -1294,7 +1295,7 @@ impl Glob {
12941295 /// Uses the provided buffer to minimize allocations.
12951296 ///
12961297 /// The `normalized` path always uses forward slashes (for internal pattern matching).
1297- /// This function converts to backslashes for output on Windows when `posix: false` .
1298+ /// Output also uses forward slashes for consistent cross-platform behavior .
12981299 #[ inline]
12991300 fn build_result_path (
13001301 & self ,
@@ -1306,47 +1307,34 @@ impl Glob {
13061307 ) -> String {
13071308 // When mark:true, add trailing slash to directories but NOT to symlinks
13081309 let should_mark_as_dir = is_dir && !is_symlink && self . mark ;
1309- let use_forward = self . should_normalize_backslashes ( ) ;
1310- let sep = if use_forward { '/' } else { '\\' } ;
1311- let dot_prefix = if use_forward { "./" } else { ".\\ " } ;
13121310
13131311 if self . absolute {
13141312 // Build absolute path
13151313 result_buffer. clear ( ) ;
13161314 let abs_path = abs_cwd. join ( normalized) ;
13171315 let formatted = self . format_path_into_buffer ( & abs_path, result_buffer) ;
13181316
1319- if should_mark_as_dir && !formatted. ends_with ( '/' ) && !formatted . ends_with ( '\\' ) {
1317+ if should_mark_as_dir && !formatted. ends_with ( '/' ) {
13201318 let mut result = formatted. to_string ( ) ;
1321- result. push ( sep ) ;
1319+ result. push ( '/' ) ;
13221320 result
13231321 } else {
13241322 formatted. to_string ( )
13251323 }
13261324 } else {
1327- // Build relative path
1328- // First, convert separators if needed (normalized always uses forward slashes)
1329- let output_normalized = if use_forward {
1330- normalized. to_string ( )
1331- } else {
1332- normalized. replace ( '/' , "\\ " )
1333- } ;
1334-
1335- let base = if self . dot_relative
1336- && !output_normalized. starts_with ( "../" )
1337- && !output_normalized. starts_with ( "..\\ " )
1338- {
1325+ // Build relative path (normalized already uses forward slashes)
1326+ let base = if self . dot_relative && !normalized. starts_with ( "../" ) {
13391327 result_buffer. clear ( ) ;
1340- result_buffer. push_str ( dot_prefix ) ;
1341- result_buffer. push_str ( & output_normalized ) ;
1328+ result_buffer. push_str ( "./" ) ;
1329+ result_buffer. push_str ( normalized ) ;
13421330 result_buffer. clone ( )
13431331 } else {
1344- output_normalized
1332+ normalized . to_string ( )
13451333 } ;
13461334
1347- if should_mark_as_dir && !base. ends_with ( '/' ) && !base . ends_with ( '\\' ) {
1335+ if should_mark_as_dir && !base. ends_with ( '/' ) {
13481336 let mut result = base;
1349- result. push ( sep ) ;
1337+ result. push ( '/' ) ;
13501338 result
13511339 } else {
13521340 base
@@ -1960,38 +1948,25 @@ impl Glob {
19601948
19611949 /// Check if backslashes should be normalized to forward slashes.
19621950 ///
1963- /// On Windows, when `posix: false` (the default), paths should use native backslashes
1964- /// to match glob's behavior. On all other platforms, or when `posix: true`, we normalize
1965- /// to forward slashes .
1951+ /// Always returns true - globlin always outputs forward slashes for consistent
1952+ /// cross-platform behavior. This differs slightly from glob v13 which uses
1953+ /// backslashes on Windows by default, but provides better UX for most use cases .
19661954 #[ inline]
19671955 fn should_normalize_backslashes ( & self ) -> bool {
1968- // On Windows with posix: false, keep backslashes
1969- // Otherwise, normalize to forward slashes
1970- self . posix || !cfg ! ( target_os = "windows" )
1956+ // Always normalize to forward slashes for consistent cross-platform output
1957+ true
19711958 }
19721959
1973- /// Normalize path separators based on platform and posix option.
1974- ///
1975- /// When use_forward_slashes is true: converts backslashes to forward slashes
1976- /// When use_forward_slashes is false: converts forward slashes to backslashes
1960+ /// Normalize path separators to forward slashes.
19771961 ///
1962+ /// Always converts backslashes to forward slashes for consistent output.
19781963 /// Returns the original string if no conversion is needed.
19791964 #[ inline]
19801965 fn normalize_separators < ' a > ( & self , path : & ' a str ) -> Cow < ' a , str > {
1981- let use_forward = self . should_normalize_backslashes ( ) ;
1982- if use_forward {
1983- if !path. contains ( '\\' ) {
1984- Cow :: Borrowed ( path)
1985- } else {
1986- Cow :: Owned ( path. replace ( '\\' , "/" ) )
1987- }
1966+ if !path. contains ( '\\' ) {
1967+ Cow :: Borrowed ( path)
19881968 } else {
1989- // On Windows with posix: false, convert forward slashes to backslashes
1990- if !path. contains ( '/' ) {
1991- Cow :: Borrowed ( path)
1992- } else {
1993- Cow :: Owned ( path. replace ( '/' , "\\ " ) )
1994- }
1969+ Cow :: Owned ( path. replace ( '\\' , "/" ) )
19951970 }
19961971 }
19971972
@@ -2083,23 +2058,13 @@ impl Glob {
20832058 formatted
20842059 }
20852060 } else {
2086- let sep = if self . should_normalize_backslashes ( ) {
2087- '/'
2088- } else {
2089- '\\'
2090- } ;
20912061 let base = if self . dot_relative {
2092- format ! ( ".{sep} {file_name}" )
2062+ format ! ( "./ {file_name}" )
20932063 } else {
20942064 file_name. clone ( )
20952065 } ;
2096- if self . mark
2097- && is_dir
2098- && !is_symlink
2099- && !base. ends_with ( '/' )
2100- && !base. ends_with ( '\\' )
2101- {
2102- format ! ( "{base}{sep}" )
2066+ if self . mark && is_dir && !is_symlink && !base. ends_with ( '/' ) {
2067+ format ! ( "{base}/" )
21032068 } else {
21042069 base
21052070 }
@@ -2186,29 +2151,14 @@ impl Glob {
21862151 formatted
21872152 }
21882153 } else {
2189- let use_forward = self . should_normalize_backslashes ( ) ;
2190- let sep = if use_forward { '/' } else { '\\' } ;
2191- // Convert separators for output (static_path uses forward slashes internally)
2192- let output_base = if use_forward {
2193- base_path. to_string ( )
2154+ // Static path already uses forward slashes internally
2155+ let base = if self . dot_relative && !base_path. starts_with ( "../" ) {
2156+ format ! ( "./{base_path}" )
21942157 } else {
2195- base_path. replace ( '/' , "\\ " )
2196- } ;
2197- let base = if self . dot_relative
2198- && !output_base. starts_with ( "../" )
2199- && !output_base. starts_with ( "..\\ " )
2200- {
2201- format ! ( ".{sep}{output_base}" )
2202- } else {
2203- output_base
2158+ base_path. to_string ( )
22042159 } ;
2205- if self . mark
2206- && is_dir
2207- && !is_symlink
2208- && !base. ends_with ( '/' )
2209- && !base. ends_with ( '\\' )
2210- {
2211- format ! ( "{base}{sep}" )
2160+ if self . mark && is_dir && !is_symlink && !base. ends_with ( '/' ) {
2161+ format ! ( "{base}/" )
22122162 } else {
22132163 base
22142164 }
0 commit comments