Create a normalized, absolute path from any path.
This is a function that's like std::fs::canonicalize
but does not touch the filesystem and therefore will work with paths that may not exist (or may error for other reasons). It does still require the environment for getting the current directory. Also, like canonicalize
(and unlike components
) the implementation is platform specific.
Proposed POSIX implementation (Playground):
/// Create a normalized absolute path without accessing the filesystem.
fn resolve(path: &Path) -> io::Result<PathBuf> {
// This is mostly a wrapper around collecting `Path::components`, with
// exceptions made where this conflicts with the POSIX specification.
// See 4.13 Pathname Resolution, IEEE Std 1003.1-2017
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13
let mut components = path.components();
let path_os = path.as_os_str().as_bytes();
// "A null pathname shall not be successfully resolved."
if path_os.is_empty() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"cannot normalize an empty path",
));
}
let mut resolved = if path.is_absolute() {
// "If a pathname begins with two successive <slash> characters, the
// first component following the leading <slash> characters may be
// interpreted in an implementation-defined manner, although more than
// two leading <slash> characters shall be treated as a single <slash>
// character."
if path_os.starts_with(b"//") && !path_os.starts_with(b"///") {
components.next();
PathBuf::from("//")
} else {
PathBuf::new()
}
} else {
env::current_dir()?
};
resolved.extend(components);
// "Interfaces using pathname resolution may specify additional constraints
// when a pathname that does not name an existing directory contains at
// least one non- <slash> character and contains one or more trailing
// <slash> characters".
// A trailing <slash> is also meaningful if "a symbolic link is
// encountered during pathname resolution".
if path_os.ends_with(b"/") {
resolved.push("");
}
Ok(resolved)
}
The name could probably do with a few rounds of bikeshedding but I think it gets the idea across well enough for an initial proposal. It's in the path
module (not fs
) and resolves the path to an absolute one in a similar way to canonicalize does with the exception that it does not attempt to resolve symlinks (therefore any ..
components are preserved).
A major criticism of an earlier proposal was it could change the semantics of paths. This attempts to stick to the POSIX specification to make sure the output path is equivalent to the input according to the rules for path resolution.