///|
pub fn decode_percent_path(path : String) -> String {
let chars = string_chars(path)
let mut out = ""
let mut index = 0
while index < chars.length() {
let ch = chars[index]
if ch == '%' && index + 2 < chars.length() {
let hi = hex_value(chars[index + 1])
let lo = hex_value(chars[index + 2])
match (hi, lo) {
(Some(a), Some(b)) => {
out = out + [Int::unsafe_to_char(a * 16 + b)]
index += 3
continue
}
_ => ()
}
}
out = out + [ch]
index += 1
}
out
}
///|
pub fn encode_path_spaces(path : String) -> String {
let mut out = ""
for _, ch in path {
if ch == ' ' {
out = out + "%20"
} else {
out = out + [ch]
}
}
out
}
///|
pub fn hex_value(ch : Char) -> Int? {
if ch >= '0' && ch <= '9' {
Some(ch.to_int() - '0'.to_int())
} else if ch >= 'a' && ch <= 'f' {
Some(ch.to_int() - 'a'.to_int() + 10)
} else if ch >= 'A' && ch <= 'F' {
Some(ch.to_int() - 'A'.to_int() + 10)
} else {
None
}
}
///|
pub fn has_percent_escape(text : String) -> Bool {
let chars = string_chars(text)
for index = 0; index + 2 < chars.length(); index = index + 1 {
if chars[index] == '%' &&
hex_value(chars[index + 1]) is Some(_) &&
hex_value(chars[index + 2]) is Some(_) {
return true
}
}
false
}
///|
pub fn invalid_percent_offsets(text : String) -> Array[Int] {
let chars = string_chars(text)
let offsets : Array[Int] = []
let mut index = 0
while index < chars.length() {
if chars[index] == '%' {
if index + 2 >= chars.length() ||
hex_value(chars[index + 1]) is None ||
hex_value(chars[index + 2]) is None {
offsets.push(index)
index += 1
} else {
index += 3
}
} else {
index += 1
}
}
offsets
}
///|
pub fn percent_report(text : String) -> String {
let bad = invalid_percent_offsets(text)
if bad.is_empty() {
"ok"
} else {
"invalid percent escapes at " + join_ints(bad, ",")
}
}
///|
pub fn join_ints(values : Array[Int], sep : String) -> String {
let parts : Array[String] = []
for value in values {
parts.push(value.to_string())
}
join_with(parts, sep)
}
///|
pub fn canonical_match_path(path : String) -> String {
clean_path(encode_path_spaces(decode_percent_path(url_path(path))))
}
///|
pub fn string_chars(text : String) -> Array[Char] {
let chars : Array[Char] = []
for _, ch in text {
chars.push(ch)
}
chars
}