///|
pub fn normalize(path : String) -> String raise {
if path.has_prefix("/") ||
path.contains("\\") ||
path.contains(":") ||
path.contains("\u0000") {
raise @config.Invalid("Expected a confined relative path: " + path)
}
let parts = []
for part in path.split("/") {
let part = part.to_owned()
if part == ".." {
raise @config.Invalid("Path escapes its root: " + path)
}
if part != "" && part != "." {
parts.push(part)
}
}
parts.join("/")
}
///|
pub fn join(root : String, path : String) -> String raise {
let root = normalize(root)
let path = normalize(path)
if root == "" {
path
} else if path == "" {
root
} else {
root + "/" + path
}
}
///|
pub fn within(root : String, path : String) -> Bool {
root == "" || path == root || path.has_prefix(root + "/")
}
///|
pub fn parent(path : String) -> String {
let parts = path.split("/").map(StringView::to_owned).collect()
if parts.length() <= 1 {
""
} else {
parts[:parts.length() - 1].to_owned().join("/")
}
}
///|
fn suffix(root : String, path : String) -> String {
if root == "" {
path
} else if path == root {
""
} else {
path[root.length() + 1:].to_owned()
}
}