Just ran into this. I don't have a strong opinion about whether this should be in the std library, but I would like save 30-90 minutes for the people who have this need and find this page by searching.
First of all the easy one, appending:
use std::ffi::{OsStr, OsString};
use std::path::{PathBuf, Path};
/// Returns a path with a new dotted extension component appended to the end.
/// Note: does not check if the path is a file or directory; you should do that.
/// # Example
/// ```
/// use pathext::append_ext;
/// use std::path::PathBuf;
/// let path = PathBuf::from("foo/bar/baz.txt");
/// if !path.is_dir() {
/// assert_eq!(append_ext("app", path), PathBuf::from("foo/bar/baz.txt.app"));
/// }
/// ```
///
pub fn append_ext(ext: impl AsRef<OsStr>, path: PathBuf) -> PathBuf {
let mut os_string: OsString = path.into();
os_string.push(".");
os_string.push(ext.as_ref());
os_string.into()
}
Prepending is significantly harder, and I couldn't find a portable way to split an OsString
, so I gave up making a function that inserts something just after the first component of a dotted filename, and made something that can insert just before the last component of a dotted filename.
/// Prepends something just in front of the very last dot component of your extension.
/// # Example
/// ```
/// use pathext::prepend_ext;
/// use std::path::PathBuf;
/// let path = PathBuf::from("foo/bar/baz.txt.gz");
/// if !path.is_dir() {
/// assert_eq!(prepend_ext("tmp", &path), PathBuf::from("foo/bar/baz.txt.tmp.gz"));
/// }
/// let no_ext = PathBuf::from("foo/bar");
/// assert_eq!(prepend_ext("tmp", &no_ext), PathBuf::from("foo/bar.tmp"));
/// ```
///
pub fn prepend_ext(ext: impl AsRef<OsStr>, path: &Path) -> PathBuf {
let mut parent : PathBuf = path.parent().unwrap().to_owned().into();
let stem = path.file_stem().unwrap();
let orig_ext = path.extension();
parent.push(stem);
let mut parent_string : OsString = parent.into();
parent_string.push(".");
parent_string.push(ext.as_ref().to_owned());
if (orig_ext.is_some()) {
parent_string.push(".");
parent_string.push(orig_ext.unwrap().to_owned());
}
parent_string.into()
}