diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 56ea51226f9d5..2caf8832294c6 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1498,6 +1498,80 @@ impl PathBuf { true } + /// Updates [`self.extension`] to `Some(extension)` or to `None` if + /// `extension` is empty. + /// + /// Returns `false` and does nothing if [`self.file_name`] is [`None`], + /// returns `true` and updates the extension otherwise. + /// + /// If [`self.extension`] is [`None`], the extension is added; otherwise + /// it is replaced. + /// + /// If `extension` is the empty string, [`self.extension`] will be [`None`] + /// afterwards, not `Some("")`. + /// + /// # Caveats + /// + /// The new `extension` may contain dots and will be used in its entirety, + /// but only the part after the final dot will be reflected in + /// [`self.extension`]. + /// + /// If the file stem contains internal dots and `extension` is empty, part + /// of the old file stem will be considered the new [`self.extension`]. + /// + /// See the examples below. + /// + /// [`self.file_name`]: Path::file_name + /// [`self.extension`]: Path::extension + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, PathBuf}; + /// + /// let mut p = PathBuf::from("/feel/the"); + /// + /// p.add_extension("formatted"); + /// assert_eq!(Path::new("/feel/the.formatted"), p.as_path()); + /// + /// p.add_extension("dark.side"); + /// assert_eq!(Path::new("/feel/the.formatted.dark.side"), p.as_path()); + /// + /// p.set_extension("cookie"); + /// assert_eq!(Path::new("/feel/the.formatted.dark.cookie"), p.as_path()); + /// + /// p.set_extension(""); + /// assert_eq!(Path::new("/feel/the.formatted.dark"), p.as_path()); + /// p.set_extension(""); + /// assert_eq!(Path::new("/feel/the.formatted"), p.as_path()); + /// p.set_extension(""); + /// assert_eq!(Path::new("/feel/the"), p.as_path()); + /// p.set_extension(""); + /// assert_eq!(Path::new("/feel/the"), p.as_path()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn add_extension>(&mut self, extension: S) -> bool { + self._add_extension(extension.as_ref()) + } + + fn _add_extension(&mut self, extension: &OsStr) -> bool { + if self.file_stem().is_none() { + return false; + } + + // add the new extension, if any + let v = self.as_mut_vec(); + let new = extension.as_encoded_bytes(); + if !new.is_empty() { + v.reserve_exact(new.len() + 1); + v.push(b'.'); + v.extend_from_slice(new); + } + + true + } + + /// Yields a mutable reference to the underlying [`OsString`] instance. /// /// # Examples