@@ -11,7 +11,10 @@ use core::{
11
11
ops:: Deref ,
12
12
} ;
13
13
use serde:: { de:: Visitor , Deserialize , Serialize } ;
14
- use std:: path:: { Path , PathBuf } ;
14
+ use std:: {
15
+ borrow:: Cow ,
16
+ path:: { Path , PathBuf } ,
17
+ } ;
15
18
use thiserror:: Error ;
16
19
17
20
/// Represents a path to an asset in a "virtual filesystem".
@@ -129,7 +132,7 @@ impl<'a> AssetPath<'a> {
129
132
Some ( source) => AssetSourceId :: Name ( CowArc :: Borrowed ( source) ) ,
130
133
None => AssetSourceId :: Default ,
131
134
} ,
132
- path : maybe_normalize_path :: < ' a , _ > ( CowArc :: Borrowed ( path) ) ,
135
+ path : normalize_atomicow_path ( CowArc :: Borrowed ( path) ) ,
133
136
label : label. map ( CowArc :: Borrowed ) ,
134
137
} )
135
138
}
@@ -227,7 +230,7 @@ impl<'a> AssetPath<'a> {
227
230
#[ inline]
228
231
pub fn from_path_buf ( path_buf : PathBuf ) -> AssetPath < ' a > {
229
232
AssetPath {
230
- path : maybe_normalize_path ( CowArc :: < ' _ , Path > :: Owned ( path_buf. into ( ) ) ) ,
233
+ path : normalize_atomicow_path ( CowArc :: Owned ( path_buf. into ( ) ) ) ,
231
234
source : AssetSourceId :: Default ,
232
235
label : None ,
233
236
}
@@ -237,7 +240,7 @@ impl<'a> AssetPath<'a> {
237
240
#[ inline]
238
241
pub fn from_path ( path : & ' a Path ) -> AssetPath < ' a > {
239
242
AssetPath {
240
- path : maybe_normalize_path ( CowArc :: Borrowed ( path) ) ,
243
+ path : normalize_atomicow_path ( CowArc :: Borrowed ( path) ) ,
241
244
source : AssetSourceId :: Default ,
242
245
label : None ,
243
246
}
@@ -346,7 +349,7 @@ impl<'a> AssetPath<'a> {
346
349
pub fn normalized ( self ) -> AssetPath < ' a > {
347
350
AssetPath {
348
351
source : self . source ,
349
- path : maybe_normalize_path ( self . path ) ,
352
+ path : normalize_atomicow_path ( self . path ) ,
350
353
label : self . label ,
351
354
}
352
355
}
@@ -460,14 +463,20 @@ impl<'a> AssetPath<'a> {
460
463
PathBuf :: new ( )
461
464
} ;
462
465
result_path. push ( rpath) ;
463
- result_path = normalize_path ( result_path. as_path ( ) ) ;
466
+
467
+ // Boxing the result_path into a CowArc<Path> after normalization to
468
+ // avoid a potential unnecessary allocation.
469
+ let path: CowArc < Path > = maybe_normalize_path ( & result_path) . map_or_else (
470
+ || CowArc :: Owned ( result_path. into ( ) ) ,
471
+ |path| CowArc :: Owned ( path. into ( ) ) ,
472
+ ) ;
464
473
465
474
Ok ( AssetPath {
466
475
source : match source {
467
476
Some ( source) => AssetSourceId :: Name ( CowArc :: Owned ( source. into ( ) ) ) ,
468
477
None => self . source . clone_owned ( ) ,
469
478
} ,
470
- path : CowArc :: Owned ( result_path . into ( ) ) ,
479
+ path,
471
480
label : rlabel. map ( |l| CowArc :: Owned ( l. into ( ) ) ) ,
472
481
} )
473
482
}
@@ -556,7 +565,7 @@ impl From<&'static str> for AssetPath<'static> {
556
565
let ( source, path, label) = Self :: parse_internal ( asset_path) . unwrap ( ) ;
557
566
AssetPath {
558
567
source : source. into ( ) ,
559
- path : maybe_normalize_path ( CowArc :: Static ( path) ) ,
568
+ path : normalize_atomicow_path ( CowArc :: Static ( path) ) ,
560
569
label : label. map ( CowArc :: Static ) ,
561
570
}
562
571
}
@@ -581,7 +590,7 @@ impl From<&'static Path> for AssetPath<'static> {
581
590
fn from ( path : & ' static Path ) -> Self {
582
591
Self {
583
592
source : AssetSourceId :: Default ,
584
- path : maybe_normalize_path ( CowArc :: Static ( path) ) ,
593
+ path : normalize_atomicow_path ( CowArc :: Static ( path) ) ,
585
594
label : None ,
586
595
}
587
596
}
@@ -593,7 +602,7 @@ impl From<PathBuf> for AssetPath<'static> {
593
602
let path: CowArc < ' _ , Path > = path. into ( ) ;
594
603
Self {
595
604
source : AssetSourceId :: Default ,
596
- path : maybe_normalize_path ( path) ,
605
+ path : normalize_atomicow_path ( path) ,
597
606
label : None ,
598
607
}
599
608
}
@@ -655,29 +664,17 @@ impl<'de> Visitor<'de> for AssetPathVisitor {
655
664
656
665
/// Normalizes the path by collapsing all occurrences of '.' and '..' dot-segments where possible
657
666
/// as per [RFC 1808](https://datatracker.ietf.org/doc/html/rfc1808)
658
- pub ( crate ) fn normalize_path ( path : & Path ) -> PathBuf {
659
- let mut result_path = PathBuf :: new ( ) ;
660
- for elt in path. iter ( ) {
661
- if elt == "." {
662
- // Skip
663
- } else if elt == ".." {
664
- if !result_path. pop ( ) {
665
- // Preserve ".." if insufficient matches (per RFC 1808).
666
- result_path. push ( elt) ;
667
- }
668
- } else {
669
- result_path. push ( elt) ;
670
- }
667
+ pub ( crate ) fn normalize_path ( path : & Path ) -> Cow < ' _ , Path > {
668
+ match maybe_normalize_path ( path) {
669
+ Some ( pathbuf) => Cow :: Owned ( pathbuf) ,
670
+ None => Cow :: Borrowed ( path) ,
671
671
}
672
- result_path
673
672
}
674
673
675
674
/// Normalizes the path by collapsing all occurrences of '.' and '..' dot-segments where possible
676
675
/// as per [RFC 1808](https://datatracker.ietf.org/doc/html/rfc1808)
677
- /// Returns a borrowed path if no normalization was necessary, otherwise returns an owned normalized path.
678
- pub ( crate ) fn maybe_normalize_path < ' a , P : AsRef < Path > + Into < CowArc < ' a , Path > > + ' a > (
679
- as_path : P ,
680
- ) -> CowArc < ' a , Path > {
676
+ /// Returns `None` if no normalization was performed, otherwise returns a normalized `PathBuf`.
677
+ pub ( crate ) fn maybe_normalize_path < ' a , P : AsRef < Path > + ' a > ( as_path : P ) -> Option < PathBuf > {
681
678
let path = as_path. as_ref ( ) ;
682
679
let mut result_path: core:: cell:: OnceCell < PathBuf > = core:: cell:: OnceCell :: new ( ) ;
683
680
let init = |i : usize | -> PathBuf { path. iter ( ) . take ( i) . collect ( ) } ;
@@ -699,30 +696,37 @@ pub(crate) fn maybe_normalize_path<'a, P: AsRef<Path> + Into<CowArc<'a, Path>> +
699
696
}
700
697
}
701
698
702
- match result_path. into_inner ( ) {
703
- Some ( path_buf) => CowArc :: Owned ( path_buf. into ( ) ) ,
704
- None => as_path. into ( ) ,
699
+ result_path. into_inner ( )
700
+ }
701
+
702
+ pub ( crate ) fn normalize_atomicow_path < ' a > ( path : CowArc < ' a , Path > ) -> CowArc < ' a , Path > {
703
+ match maybe_normalize_path ( & path) {
704
+ Some ( normalized) => CowArc :: Owned ( normalized. into ( ) ) ,
705
+ None => path,
705
706
}
706
707
}
707
708
708
709
#[ cfg( test) ]
709
710
mod tests {
710
- use crate :: { path :: maybe_normalize_path , AssetPath } ;
711
+ use crate :: { normalize_atomicow_path , AssetPath } ;
711
712
use alloc:: string:: ToString ;
712
713
use atomicow:: CowArc ;
713
714
use std:: path:: Path ;
714
715
715
716
#[ test]
716
717
fn normalize_cow_paths ( ) {
717
- let path: CowArc < Path > = "a/../a/b" . into ( ) ;
718
+ let path: CowArc < Path > = Path :: new ( "a/../a/b" ) . into ( ) ;
718
719
719
720
assert_eq ! (
720
- maybe_normalize_path ( path) ,
721
- CowArc :: Owned ( Path :: new( "a/b" ) . into( ) )
721
+ normalize_atomicow_path ( path) ,
722
+ CowArc :: < Path > :: Owned ( Path :: new( "a/b" ) . into( ) )
722
723
) ;
723
724
724
- let path: CowArc < Path > = "a/b" . into ( ) ;
725
- assert_eq ! ( maybe_normalize_path( path) , CowArc :: Static ( Path :: new( "a/b" ) ) ) ;
725
+ let path: CowArc < Path > = Path :: new ( "a/b" ) . into ( ) ;
726
+ assert_eq ! (
727
+ normalize_atomicow_path( path) ,
728
+ CowArc :: Static ( Path :: new( "a/b" ) )
729
+ ) ;
726
730
727
731
let path = "a/b" ;
728
732
let donor = 3 ;
@@ -731,7 +735,7 @@ mod tests {
731
735
}
732
736
let path = CowArc :: < Path > :: Borrowed ( steal_lifetime ( & donor, Path :: new ( path) ) ) ;
733
737
assert_eq ! (
734
- maybe_normalize_path ( path) ,
738
+ normalize_atomicow_path ( path) ,
735
739
CowArc :: Borrowed ( Path :: new( "a/b" ) )
736
740
) ;
737
741
}
0 commit comments