///|
pub fn clean_path(path : String) -> String {
let normalized = normalize_path(url_path_without_query(path))
let parts = split_char(normalized, '/')
let stack : Array[String] = []
for part in parts {
if part == "" || part == "." {
continue
} else if part == ".." {
if !stack.is_empty() {
ignore(stack.pop())
}
} else {
stack.push(part)
}
}
"/" + join_with(stack, "/")
}
///|
pub fn path_depth(path : String) -> Int {
let clean = clean_path(path)
if clean == "/" {
return 0
}
let mut depth = 0
for part in split_char(clean, '/') {
if part != "" {
depth += 1
}
}
depth
}
///|
pub fn path_parent(path : String) -> String {
let clean = clean_path(path)
if clean == "/" {
return "/"
}
let parts = split_char(clean, '/')
let kept : Array[String] = []
for index = 0; index < parts.length() - 1; index = index + 1 {
if parts[index] != "" {
kept.push(parts[index])
}
}
if kept.is_empty() {
"/"
} else {
"/" + join_with(kept, "/")
}
}
///|
pub fn path_basename(path : String) -> String {
let clean = clean_path(path)
if clean == "/" {
return ""
}
let parts = split_char(clean, '/')
for index = parts.length() - 1; index >= 0; index = index - 1 {
if parts[index] != "" {
return parts[index]
}
}
""
}
///|
pub fn path_extension(path : String) -> String {
let name = path_basename(path)
match name.find(".") {
Some(_) => {
let mut ext = ""
let mut seen_dot = false
for _, ch in name {
if ch == '.' {
seen_dot = true
ext = ""
} else if seen_dot {
ext = ext + [ch]
}
}
ext
}
None => ""
}
}
///|
pub fn path_has_extension(path : String, ext : String) -> Bool {
lower_ascii(path_extension(path)) == lower_ascii(ext)
}
///|
pub fn remove_duplicate_paths(paths : Array[String]) -> Array[String] {
let result : Array[String] = []
for path in paths {
let clean = clean_path(path)
if !contains_string(result, clean) {
result.push(clean)
}
}
result
}
///|
pub fn common_path_prefix(paths : Array[String]) -> String {
if paths.is_empty() {
return "/"
}
let first = clean_path(paths[0])
let mut prefix = first
for index = 1; index < paths.length(); index = index + 1 {
prefix = common_two_path_prefix(prefix, clean_path(paths[index]))
}
prefix
}
///|
pub fn common_two_path_prefix(left : String, right : String) -> String {
let a = split_char(clean_path(left), '/')
let b = split_char(clean_path(right), '/')
let kept : Array[String] = []
let mut index = 0
while index < a.length() && index < b.length() {
if a[index] != "" && a[index] == b[index] {
kept.push(a[index])
} else if a[index] != "" || b[index] != "" {
break
}
index += 1
}
if kept.is_empty() {
"/"
} else {
"/" + join_with(kept, "/")
}
}
///|
pub fn path_score(path : String) -> Int {
let clean = clean_path(path)
clean.length() + path_depth(clean) * 10
}
///|
pub fn sort_paths_by_depth(paths : Array[String]) -> Array[String] {
let result = remove_duplicate_paths(paths)
for i = 0; i < result.length(); i = i + 1 {
for j = i + 1; j < result.length(); j = j + 1 {
if path_score(result[j]) < path_score(result[i]) {
let tmp = result[i]
result[i] = result[j]
result[j] = tmp
}
}
}
result
}
///|
pub fn classify_path(path : String) -> String {
let clean = clean_path(path)
if clean == "/" {
"root"
} else if path_has_extension(clean, "html") ||
path_has_extension(clean, "htm") {
"page"
} else if path_has_extension(clean, "xml") {
"xml"
} else if path_has_extension(clean, "json") {
"json"
} else if path_has_extension(clean, "png") ||
path_has_extension(clean, "jpg") ||
path_has_extension(clean, "jpeg") ||
path_has_extension(clean, "gif") {
"image"
} else if clean.has_suffix("/") {
"directory"
} else {
"resource"
}
}