@@ -1403,6 +1403,80 @@ impl PathBuf {
1403
1403
}
1404
1404
}
1405
1405
1406
+ /// Sets whether the path has a trailing [separator](MAIN_SEPARATOR).
1407
+ ///
1408
+ /// The value returned by [`has_trailing_sep`](Self::has_trailing_sep) will be equivalent to
1409
+ /// the provided value.
1410
+ ///
1411
+ /// # Examples
1412
+ ///
1413
+ /// ```
1414
+ /// let mut p = PathBuf::from("dir");
1415
+ ///
1416
+ /// assert!(!p.has_trailing_sep());
1417
+ /// p.set_trailing_sep(false);
1418
+ /// assert!(!p.has_trailing_sep());
1419
+ /// p.set_trailing_sep(true);
1420
+ /// assert!(p.has_trailing_sep());
1421
+ /// assert_eq!(p.file_name(), None);
1422
+ /// p.set_trailing_sep(false);
1423
+ /// assert!(!p.has_trailing_sep());
1424
+ /// assert_eq!(p.file_name(), Some("dir"));
1425
+ /// assert_eq!(p, Path::new("dir"));
1426
+ /// ```
1427
+ #[ unstable( feature = "path_trailing_sep" , issue = "142503" ) ]
1428
+ pub fn set_trailing_sep ( & mut self , trailing_sep : bool ) {
1429
+ if trailing_sep { self . push_trailing_sep ( ) } else { self . pop_trailing_sep ( ) }
1430
+ }
1431
+
1432
+ /// Adds a trailing [separator](MAIN_SEPARATOR) to the path.
1433
+ ///
1434
+ /// This acts similarly to [`Path::with_trailing_sep`], but mutates the underlying `PathBuf`.
1435
+ ///
1436
+ /// # Examples
1437
+ ///
1438
+ /// ```
1439
+ /// use std::path::{Path, PathBuf};
1440
+ ///
1441
+ /// let mut p = PathBuf::from("dir");
1442
+ ///
1443
+ /// assert_eq!(p.file_name(), Some(Path::new("dir")));
1444
+ /// p.push_trailing_sep();
1445
+ /// assert_eq!(p.file_name(), None);
1446
+ /// p.push_trailing_sep();
1447
+ /// assert_eq!(p.file_name(), None);
1448
+ /// assert_eq!(p, Path::new("dir/"));
1449
+ /// ```
1450
+ #[ unstable( feature = "path_trailing_sep" , issue = "142503" ) ]
1451
+ pub fn push_trailing_sep ( & mut self ) {
1452
+ if !self . has_trailing_sep ( ) {
1453
+ self . push ( "" ) ;
1454
+ }
1455
+ }
1456
+
1457
+ /// Removes a trailing [separator](MAIN_SEPARATOR) from the path, if possible.
1458
+ ///
1459
+ /// This acts similarly to [`Path::trim_trailing_sep`], but mutates the underlying `PathBuf`.
1460
+ ///
1461
+ /// # Examples
1462
+ ///
1463
+ /// ```
1464
+ /// use std::path::{Path, PathBuf};
1465
+ ///
1466
+ /// let mut p = PathBuf::from("dir/");
1467
+ ///
1468
+ /// assert_eq!(p.file_name(), None);
1469
+ /// p.pop_trailing_sep();
1470
+ /// assert_eq!(p.file_name(), Some("dir"));
1471
+ /// p.pop_trailing_sep();
1472
+ /// assert_eq!(p.file_name(), Some("dir"));
1473
+ /// assert_eq!(p, Path::new("/dir"));
1474
+ /// ```
1475
+ #[ unstable( feature = "path_trailing_sep" , issue = "142503" ) ]
1476
+ pub fn pop_trailing_sep ( & mut self ) {
1477
+ self . inner . truncate ( self . trim_trailing_sep ( ) . as_os_str ( ) . len ( ) ) ;
1478
+ }
1479
+
1406
1480
/// Updates [`self.file_name`] to `file_name`.
1407
1481
///
1408
1482
/// If [`self.file_name`] was [`None`], this is equivalent to pushing
@@ -2686,6 +2760,85 @@ impl Path {
2686
2760
self . file_name ( ) . map ( rsplit_file_at_dot) . and_then ( |( before, after) | before. and ( after) )
2687
2761
}
2688
2762
2763
+ /// Checks whether the path ends in a trailing [separator](MAIN_SEPARATOR).
2764
+ ///
2765
+ /// This is generally done to ensure that a path is treated as a directory, not a file,
2766
+ /// although it does not actually guarantee that such a path is a directory on the underlying
2767
+ /// file system.
2768
+ ///
2769
+ /// Despite this behavior, two paths are still considered the same in Rust whether they have a
2770
+ /// trailing separator or not.
2771
+ ///
2772
+ /// # Examples
2773
+ ///
2774
+ /// ```
2775
+ /// use std::path::Path;
2776
+ ///
2777
+ /// assert!(Path::new("dir/").has_trailing_sep());
2778
+ /// assert!(!Path::new("file.rs").has_trailing_sep());
2779
+ /// ```
2780
+ #[ unstable( feature = "path_trailing_sep" , issue = "142503" ) ]
2781
+ #[ must_use]
2782
+ #[ inline]
2783
+ pub fn has_trailing_sep ( & self ) -> bool {
2784
+ self . as_os_str ( ) . as_encoded_bytes ( ) . last ( ) . copied ( ) . is_some_and ( is_sep_byte)
2785
+ }
2786
+
2787
+ /// Ensures that a path has a trailing [separator](MAIN_SEPARATOR),
2788
+ /// allocating a [`PathBuf`] if necessary.
2789
+ ///
2790
+ /// The resulting path will return true for [`has_trailing_sep`](Self::has_trailing_sep) and
2791
+ /// `None` for [`file_name`](Self::file_name).
2792
+ ///
2793
+ /// # Examples
2794
+ ///
2795
+ /// ```
2796
+ /// use std::path::Path;
2797
+ ///
2798
+ /// assert_eq!(Path::new("dir//").with_trailing_sep().file_name(), None);
2799
+ /// assert_eq!(Path::new("dir/").with_trailing_sep().file_name(), None);
2800
+ /// assert_eq!(Path::new("dir").with_trailing_sep().file_name(), None);
2801
+ /// assert_eq!(Path::new("dir").file_name(), Some("dir"));
2802
+ /// ```
2803
+ #[ unstable( feature = "path_trailing_sep" , issue = "142503" ) ]
2804
+ #[ must_use]
2805
+ #[ inline]
2806
+ pub fn with_trailing_sep ( & self ) -> Cow < ' _ , Path > {
2807
+ if self . has_trailing_sep ( ) { Cow :: Borrowed ( self ) } else { Cow :: Owned ( self . join ( "" ) ) }
2808
+ }
2809
+
2810
+ /// Trims a trailing [separator](MAIN_SEPARATOR) from a path, if possible.
2811
+ ///
2812
+ /// The resulting path will return false for [`has_trailing_sep`](Self::has_trailing_sep).
2813
+ ///
2814
+ /// # Examples
2815
+ ///
2816
+ /// ```
2817
+ /// use std::path::Path;
2818
+ ///
2819
+ /// assert_eq!(Path::new("dir//").trim_trailing_sep().file_name(), Some("dir"));
2820
+ /// assert_eq!(Path::new("dir/").trim_trailing_sep().file_name(), Some("dir"));
2821
+ /// assert_eq!(Path::new("dir").trim_trailing_sep().file_name(), Some("dir"));
2822
+ /// ```
2823
+ #[ unstable( feature = "path_trailing_sep" , issue = "142503" ) ]
2824
+ #[ must_use]
2825
+ #[ inline]
2826
+ pub fn trim_trailing_sep ( & self ) -> & Path {
2827
+ if self . has_trailing_sep ( ) && ( !self . has_root ( ) || self . parent ( ) . is_some ( ) ) {
2828
+ let mut bytes = self . inner . as_encoded_bytes ( ) ;
2829
+ while let Some ( ( last, init) ) = bytes. split_last ( )
2830
+ && is_sep_byte ( * last)
2831
+ {
2832
+ bytes = init;
2833
+ }
2834
+
2835
+ // SAFETY: Trimming trailing ASCII bytes will retain the validity of the string.
2836
+ Path :: new ( unsafe { OsStr :: from_encoded_bytes_unchecked ( bytes) } )
2837
+ } else {
2838
+ self
2839
+ }
2840
+ }
2841
+
2689
2842
/// Creates an owned [`PathBuf`] with `path` adjoined to `self`.
2690
2843
///
2691
2844
/// If `path` is absolute, it replaces the current path.
0 commit comments