///|
fn char_at(s : String, index : Int) -> Char {
s.get_char(index).unwrap()
}
///|
fn is_ctl(c : Char) -> Bool {
let n = c.to_int()
(n >= 0 && n <= 31) || n == 127
}
///|
fn is_ows(c : Char) -> Bool {
c == ' ' || c == '\t'
}
///|
fn is_separator(c : Char) -> Bool {
c == '(' ||
c == ')' ||
c == '<' ||
c == '>' ||
c == '@' ||
c == ',' ||
c == ';' ||
c == ':' ||
c == '\\' ||
c == '"' ||
c == '/' ||
c == '[' ||
c == ']' ||
c == '?' ||
c == '=' ||
c == '{' ||
c == '}' ||
c == ' ' ||
c == '\t'
}
///|
pub fn is_token_char(c : Char) -> Bool {
c.is_ascii() && !is_ctl(c) && !is_separator(c)
}
///|
pub fn is_token(value : String) -> Bool {
!value.is_empty() && value.all(c => is_token_char(c))
}
///|
fn is_attr_char(c : Char) -> Bool {
c.is_ascii_alphabetic() ||
c.is_ascii_digit() ||
c == '!' ||
c == '#' ||
c == '$' ||
c == '&' ||
c == '+' ||
c == '-' ||
c == '.' ||
c == '^' ||
c == '_' ||
c == '`' ||
c == '|' ||
c == '~'
}
///|
fn is_hex(c : Char) -> Bool {
c.is_ascii_hexdigit()
}
///|
fn hex_value(c : Char) -> Int {
if c >= '0' && c <= '9' {
c.to_int() - '0'.to_int()
} else if c >= 'A' && c <= 'F' {
c.to_int() - 'A'.to_int() + 10
} else if c >= 'a' && c <= 'f' {
c.to_int() - 'a'.to_int() + 10
} else {
-1
}
}
///|
fn is_ascii_visible(c : Char) -> Bool {
c.is_ascii() && c.to_int() >= 32 && c.to_int() <= 126
}
///|
fn needs_quoted(value : String) -> Bool {
value.is_empty() || !value.all(c => is_token_char(c))
}
///|
fn needs_extended_filename(value : String) -> Bool {
value.any(c => !is_attr_char(c))
}
///|
fn append_percent_hex(buf : StringBuilder, byte : Int) -> Unit {
let upper = "0123456789ABCDEF"
buf.write_char('%')
buf.write_char(char_at(upper, (byte >> 4) & 15))
buf.write_char(char_at(upper, byte & 15))
}
///|
fn ascii_fallback_char(c : Char) -> Char {
if is_ascii_visible(c) && c != '"' && c != '\\' && c != '/' {
c
} else {
'_'
}
}
///|
fn ascii_fallback(value : String) -> String {
let buf = StringBuilder(size_hint=value.length())
for c in value.iter() {
buf.write_char(ascii_fallback_char(c))
}
buf.to_string()
}