@@ -473,57 +473,102 @@ impl PartialConfigurationExt for PartialConfiguration {
473
473
/// Normalizes a path, resolving '..' and '.' segments without requiring the path to exist
474
474
fn normalize_path ( path : & Path ) -> PathBuf {
475
475
let mut components = Vec :: new ( ) ;
476
- let mut has_root_or_prefix = false ;
476
+ let mut prefix_component = None ;
477
+ let mut is_absolute = false ;
477
478
478
479
for component in path. components ( ) {
479
480
match component {
481
+ std:: path:: Component :: Prefix ( prefix) => {
482
+ prefix_component = Some ( component) ;
483
+ components. clear ( ) ;
484
+ }
485
+ std:: path:: Component :: RootDir => {
486
+ is_absolute = true ;
487
+ components. clear ( ) ;
488
+ }
480
489
std:: path:: Component :: ParentDir => {
481
- if !components. is_empty ( )
482
- && !matches ! ( components. last( ) , Some ( c) if matches!( Path :: new( c) . components( ) . next( ) ,
483
- Some ( std:: path:: Component :: Prefix ( _) ) ) )
484
- {
490
+ if !components. is_empty ( ) {
485
491
components. pop ( ) ;
492
+ } else if !is_absolute && prefix_component. is_none ( ) {
493
+ // Only keep parent dir if we're not absolute and have no prefix
494
+ components. push ( component. as_os_str ( ) ) ;
486
495
}
487
496
}
488
- std:: path:: Component :: Normal ( c) => components. push ( c) ,
489
- std:: path:: Component :: CurDir => { }
490
- c @ std:: path:: Component :: RootDir => {
491
- has_root_or_prefix = true ;
492
- components. clear ( ) ;
493
- components. push ( c. as_os_str ( ) ) ;
497
+ std:: path:: Component :: Normal ( c) => {
498
+ components. push ( c) ;
494
499
}
495
- c @ std:: path:: Component :: Prefix ( _) => {
496
- has_root_or_prefix = true ;
497
- components. clear ( ) ;
498
- components. push ( c. as_os_str ( ) ) ;
500
+ std:: path:: Component :: CurDir => {
501
+ // Skip current directory components
499
502
}
500
503
}
501
504
}
502
505
503
- if components. is_empty ( ) {
504
- if has_root_or_prefix {
505
- // On Windows, this would be something like "C:\" or "\"
506
- path. ancestors ( )
506
+ let mut result = PathBuf :: new ( ) ;
507
+
508
+ // Add prefix component (like C: on Windows)
509
+ if let Some ( prefix) = prefix_component {
510
+ result. push ( prefix. as_os_str ( ) ) ;
511
+ }
512
+
513
+ // Add root directory if path is absolute
514
+ if is_absolute {
515
+ result. push ( std:: path:: Component :: RootDir . as_os_str ( ) ) ;
516
+ }
517
+
518
+ // Add normalized components
519
+ for component in components {
520
+ result. push ( component) ;
521
+ }
522
+
523
+ // Handle edge cases
524
+ if result. as_os_str ( ) . is_empty ( ) {
525
+ if prefix_component. is_some ( ) || is_absolute {
526
+ // This shouldn't happen with proper input, but fallback to original path's root
527
+ return path
528
+ . ancestors ( )
507
529
. last ( )
508
530
. unwrap_or ( Path :: new ( "" ) )
509
- . to_path_buf ( )
531
+ . to_path_buf ( ) ;
510
532
} else {
511
- // Return current directory as a relative path
512
- PathBuf :: from ( "." )
513
- }
514
- } else {
515
- let mut result = PathBuf :: new ( ) ;
516
- for component in components {
517
- result. push ( component) ;
533
+ return PathBuf :: from ( "." ) ;
518
534
}
519
- result
520
535
}
536
+
537
+ result
521
538
}
522
539
523
540
#[ cfg( test) ]
524
541
mod tests {
525
542
use super :: * ;
526
543
544
+ #[ test]
545
+ fn test_normalize_path_windows_drive ( ) {
546
+ if cfg ! ( windows) {
547
+ let path = Path :: new ( r"z:\workspace\test_one\..\postgrestools.jsonc" ) ;
548
+ let normalized = normalize_path ( path) ;
549
+ assert_eq ! (
550
+ normalized,
551
+ PathBuf :: from( r"z:\workspace\postgrestools.jsonc" )
552
+ ) ;
553
+ }
554
+ }
555
+
556
+ #[ test]
557
+ fn test_normalize_path_relative ( ) {
558
+ let path = Path :: new ( "workspace/test_one/../postgrestools.jsonc" ) ;
559
+ let normalized = normalize_path ( path) ;
560
+ assert_eq ! ( normalized, PathBuf :: from( "workspace/postgrestools.jsonc" ) ) ;
561
+ }
562
+
563
+ #[ test]
564
+ fn test_normalize_path_multiple_parent_dirs ( ) {
565
+ if cfg ! ( windows) {
566
+ let path = Path :: new ( r"c:\a\b\c\..\..\d" ) ;
567
+ let normalized = normalize_path ( path) ;
568
+ assert_eq ! ( normalized, PathBuf :: from( r"c:\a\d" ) ) ;
569
+ }
570
+ }
571
+
527
572
#[ test]
528
573
fn test_strip_jsonc_comments_line_comments ( ) {
529
574
let input = r#"{
0 commit comments