///|
fn clean(input : StringView) -> String {
input.trim().to_owned()
}
///|
fn split_quoted(input : String, sep : Char) -> Array[String] {
let parts : Array[String] = []
let buf = StringBuilder()
let mut quoted = false
let mut escaped = false
for c in input.iter() {
if escaped {
buf.write_char(c)
escaped = false
} else if quoted && c == '\\' {
buf.write_char(c)
escaped = true
} else if c == '"' {
quoted = !quoted
buf.write_char(c)
} else if !quoted && c == sep {
parts.push(buf.to_string())
buf.reset()
} else {
buf.write_char(c)
}
}
parts.push(buf.to_string())
parts
}
///|
fn is_ascii_alpha(c : Char) -> Bool {
let code = c.to_int()
(code >= 65 && code <= 90) || (code >= 97 && code <= 122)
}
///|
fn is_ascii_digit(c : Char) -> Bool {
let code = c.to_int()
code >= 48 && code <= 57
}
///|
fn digit_value(c : Char) -> Int? {
let code = c.to_int()
if code >= 48 && code <= 57 {
Some(code - 48)
} else {
None
}
}
///|
fn is_tchar(c : Char) -> Bool {
let code = c.to_int()
is_ascii_alpha(c) ||
is_ascii_digit(c) ||
code == 33 ||
code == 35 ||
code == 36 ||
code == 37 ||
code == 38 ||
code == 39 ||
code == 42 ||
code == 43 ||
code == 45 ||
code == 46 ||
code == 94 ||
code == 95 ||
code == 96 ||
code == 124 ||
code == 126
}
///|
fn is_token(input : String) -> Bool {
if input.is_empty() {
return false
}
for c in input.iter() {
if !is_tchar(c) {
return false
}
}
true
}
///|
fn is_language_subtag(input : String) -> Bool {
let len = input.length()
if len < 1 || len > 8 {
return false
}
for c in input.iter() {
if !(is_ascii_alpha(c) || is_ascii_digit(c)) {
return false
}
}
true
}
///|
fn unquote(input : String) -> String {
let len = input.length()
if len >= 2 &&
input.get_char(0) == Some('"') &&
input.get_char(len - 1) == Some('"') {
let inner = input[1:len - 1].to_owned()
let out = StringBuilder(size_hint=inner.length())
let mut escaped = false
for c in inner.iter() {
if escaped {
out.write_char(c)
escaped = false
} else if c == '\\' {
escaped = true
} else {
out.write_char(c)
}
}
if escaped {
out.write_char('\\')
}
out.to_string()
} else {
input
}
}
///|
fn parse_param(raw : String, item : Int) -> Result[Param, ParseError] {
let piece = clean(raw[:])
match piece.split_once("=") {
Some((name_view, value_view)) => {
let name = clean(name_view).to_lower()
let value = unquote(clean(value_view))
if !is_token(name) {
Err(InvalidToken(item, "parameter name", name))
} else {
Ok({ name, value })
}
}
None => Err(InvalidParameter(item, piece))
}
}
///|
fn parse_q(raw : String, item : Int) -> Result[Int, ParseError] {
let q = clean(raw[:])
if q == "1" {
return Ok(1000)
}
if q == "0" {
return Ok(0)
}
if q.length() >= 2 && q.get_char(1) == Some('.') {
match q.get_char(0) {
Some('0') => parse_q_fraction(q, item, false)
Some('1') => parse_q_fraction(q, item, true)
_ => Err(InvalidQ(item, q))
}
} else {
Err(InvalidQ(item, q))
}
}
///|
fn parse_q_fraction(
q : String,
item : Int,
is_one : Bool,
) -> Result[Int, ParseError] {
let mut value = 0
let mut digits = 0
for i in 2..
match digit_value(c) {
Some(d) => {
digits += 1
if digits > 3 {
return Err(InvalidQ(item, q))
}
if is_one && d != 0 {
return Err(InvalidQ(item, q))
}
value = value * 10 + d
}
None => return Err(InvalidQ(item, q))
}
None => return Err(InvalidQ(item, q))
}
}
while digits < 3 {
value = value * 10
digits += 1
}
if is_one {
Ok(1000)
} else {
Ok(value)
}
}
///|
pub fn quality_to_string(q : Int) -> String {
let clamped = if q < 0 { 0 } else if q > 1000 { 1000 } else { q }
let whole = clamped / 1000
let frac = clamped % 1000
let buf = StringBuilder(size_hint=5)
buf.write_string(whole.to_string())
buf.write_char('.')
if frac < 100 {
buf.write_char('0')
}
if frac < 10 {
buf.write_char('0')
}
buf.write_string(frac.to_string())
buf.to_string()
}
///|
fn param_value(params : Array[Param], name : String) -> String? {
let wanted = name.to_lower()
for p in params {
if p.name == wanted {
return Some(p.value)
}
}
None
}
///|
fn params_match(needed : Array[Param], offered : Array[Param]) -> Bool {
for p in needed {
match param_value(offered, p.name) {
Some(value) => if value != p.value { return false }
None => return false
}
}
true
}
///|
fn append_params(buf : StringBuilder, params : Array[Param]) -> Unit {
for p in params {
buf.write_string(";")
buf.write_string(p.name)
buf.write_string("=")
buf.write_string(p.value)
}
}