#[must_use]
pub fn doc_dir(path: &str) -> &str {
match path.rfind('/') {
Some(i) => &path[..=i],
None => "",
}
}
#[must_use]
pub fn resolve_relative(target: &str, doc_dir: &str) -> String {
if !target.starts_with("./") && !target.starts_with("../") {
return target.to_owned();
}
let joined = format!("{doc_dir}{target}");
let mut resolved: Vec<&str> = Vec::new();
for p in joined.split('/') {
match p {
"." | "" => {}
".." => {
resolved.pop();
}
seg => resolved.push(seg),
}
}
resolved.join("/")
}
#[must_use]
pub fn canonical_path(path: &str) -> &str {
path.strip_prefix('/').unwrap_or(path)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn directory_of_a_path() {
assert_eq!(doc_dir("a/b/c.md"), "a/b/");
assert_eq!(doc_dir("c.md"), "");
assert_eq!(doc_dir("a/"), "a/");
assert_eq!(doc_dir(""), "");
}
#[test]
fn relative_targets_join_the_directory() {
assert_eq!(resolve_relative("./x.md", "a/b/"), "a/b/x.md");
assert_eq!(resolve_relative("../x.md", "a/b/"), "a/x.md");
assert_eq!(resolve_relative("../../x.md", "a/b/"), "x.md");
assert_eq!(resolve_relative("../../../x.md", "a/b/"), "x.md");
assert_eq!(resolve_relative("./x.md", ""), "x.md");
assert_eq!(resolve_relative("./x/../../old.md", "a/"), "old.md");
assert_eq!(resolve_relative("./a//b/./c.md", ""), "a/b/c.md");
assert_eq!(resolve_relative("x.md", "a/b/"), "x.md");
assert_eq!(resolve_relative("/x.md", "a/b/"), "/x.md");
assert_eq!(resolve_relative("note", "a/"), "note");
assert_eq!(resolve_relative(".hidden/x.md", "a/"), ".hidden/x.md");
assert_eq!(resolve_relative("", "a/"), "");
}
#[test]
fn canonical_strips_one_slash() {
assert_eq!(canonical_path("/a.md"), "a.md");
assert_eq!(canonical_path("//a.md"), "/a.md");
assert_eq!(canonical_path("a.md"), "a.md");
}
}