Skip to main content

resolve_relative

Function resolve_relative 

Source
pub fn resolve_relative(
    base: &Path,
    relative: &str,
) -> Result<PathBuf, PathError>
Expand description

Join a portable, /-separated relative path onto base, using the platform path separator throughout.

Source documents and manifests spell relative paths with / (manifest convention). Joining such a string with Path::join directly leaves the / embedded on Windows, producing mixed paths like dir\sub/file. This rebuilds the relative portion component-by-component so the result is uniformly native-separated. A relative value that is absolute or rooted passes through unchanged; on Windows a leading / is rooted but prefix-less, which Path::is_absolute alone misses, so has_root is checked too.

Normalization is lexical and stack-based: . and empty (a//b) segments are dropped; a .. pops the last resolved name; a .. that escapes a relative base is preserved as a leading .. (proj/sub + ../../../shared/x -> ../shared/x), not silently swallowed; and a .. at the root of an absolute base is clamped (/a + ../../x -> /x), matching the OS. Nothing here touches the filesystem, so .. is resolved textually and ignores symlinks; identity-grade canonicalization (NFC, escape rejection) lives in mos_cache::ProjectPath. Each /-delimited segment must be a single portable name – manifest paths use / only, so a segment that smuggles in a platform separator (\), a drive prefix, or an absolute root is rejected (see Errors) rather than handed to PathBuf::push, which would let the OS re-split it past this lexical model.

§Errors

Returns PathError::UnsafeSegment when a /-delimited segment is not a single portable component. An absolute or rooted relative is not a segment and still passes through unchanged.

§Examples

use std::path::Path;

use mos_core::resolve_relative;

assert_eq!(
    resolve_relative(Path::new("proj"), "sub/file.txt"),
    Ok(Path::new("proj").join("sub").join("file.txt")),
);
// `.` and `..` segments are normalized away within the base:
assert_eq!(
    resolve_relative(Path::new("proj"), "sub/../assets/./logo.png"),
    Ok(Path::new("proj").join("assets").join("logo.png")),
);
// `..` past a relative base is preserved, not eaten:
assert_eq!(
    resolve_relative(Path::new("proj/sub"), "../../../shared/x"),
    Ok(Path::new("..").join("shared").join("x")),
);