///|
pub fn normalize_relative_path(input : String) -> String {
let text = input.replace_all(old="\\", new="/")
let pieces = text.split("/")
let stack : Array[String] = []
for piece in pieces {
if piece == "" || piece == "." {
continue
}
if piece == ".." {
if !stack.is_empty() {
stack.pop() |> ignore
}
} else {
stack.push(piece.to_owned())
}
}
stack.join("/")
}
///|
pub fn path_depth(path : String) -> Int {
let normalized = normalize_relative_path(path)
if normalized == "" {
0
} else {
normalized.split("/").length()
}
}
///|
pub fn basename(path : String) -> String {
let normalized = normalize_relative_path(path)
if normalized == "" {
""
} else {
let parts = normalized.split("/").to_array()
parts[parts.length() - 1].to_owned()
}
}
///|
pub fn parent_path(path : String) -> String {
let normalized = normalize_relative_path(path)
let parts = normalized.split("/").to_array()
if parts.length() <= 1 {
""
} else {
parts[:parts.length() - 1].join("/")
}
}
///|
pub fn extension_of(path : String) -> String {
let name = basename(path)
let bytes : Array[Char] = [..name]
let mut dot = -1
for i = 0; i < bytes.length(); i = i + 1 {
if bytes[i] == '.' {
dot = i
}
}
if dot <= 0 || dot == bytes.length() - 1 {
""
} else {
let suffix : String = [..bytes[dot + 1:]]
suffix.to_lower()
}
}
///|
pub fn is_hidden_path(path : String) -> Bool {
for part in normalize_relative_path(path).split("/") {
if part.length() > 1 && part.has_prefix(".") {
return true
}
}
false
}
///|
pub fn is_descendant(path : String, parent : String) -> Bool {
let child = normalize_relative_path(path)
let base = normalize_relative_path(parent)
base == "" || child.has_prefix(base + "/")
}
///|
fn wildcard_match_here(
pattern : Array[Char],
text : Array[Char],
pi : Int,
ti : Int,
) -> Bool {
if pi == pattern.length() {
return ti == text.length()
}
if pattern[pi] == '*' {
let mut next = ti
while next <= text.length() {
if wildcard_match_here(pattern, text, pi + 1, next) {
return true
}
next += 1
}
false
} else if ti < text.length() &&
(pattern[pi] == '?' || pattern[pi] == text[ti]) {
wildcard_match_here(pattern, text, pi + 1, ti + 1)
} else {
false
}
}
///|
pub fn wildcard_match(pattern : String, text : String) -> Bool {
wildcard_match_here([..pattern], [..text], 0, 0)
}
///|
pub fn matches_any_pattern(path : String, patterns : Array[String]) -> Bool {
let normalized = normalize_relative_path(path)
for pattern in patterns {
if wildcard_match(normalize_relative_path(pattern), normalized) ||
wildcard_match(normalize_relative_path(pattern), basename(normalized)) {
return true
}
}
false
}