///|
/// Helper functions
///
///|
fn acc_parse_media_range_item(
input : String,
) -> Result[MediaRange, AcceptError] {
let parts = acc_split_with_quotes(input, 59) // ;
if parts.length() == 0 {
return Err(InvalidMediaRange)
}
let media = acc_trim_string(parts[0])
let parse_result = acc_parse_media_range(media)
match parse_result {
Err(e) => return Err(e)
Ok((mt, st)) => {
let mut params : Array[(String, String)] = []
let mut qvalue = { value: 1000 }
let mut q_seen = false
let mut param_idx = 1
while param_idx < parts.length() {
let param = acc_trim_string(parts[param_idx])
if param.length() > 0 {
let eq_pos = acc_find_char(param, 61) // =
match eq_pos {
None => return Err(InvalidParameter)
Some(pos) => {
let name = acc_to_lower_case(
acc_trim_string(acc_string_slice(param, 0, pos)),
)
let value_str = acc_trim_string(
acc_string_slice_from(param, pos + 1),
)
if name == "q" {
if q_seen {
return Err(InvalidQValue)
}
let q_result = QValue::parse(value_str)
match q_result {
Ok(q) => {
qvalue = q
q_seen = true
}
Err(e) => return Err(e)
}
} else {
let value_result = acc_parse_param_value(value_str)
match value_result {
Ok(value) => {
let new_params = []
let mut j = 0
while j < params.length() {
new_params.push(params[j])
j = j + 1
}
new_params.push((name, value))
params = new_params
}
Err(e) => return Err(e)
}
}
}
}
}
param_idx = param_idx + 1
}
Ok({ media_type: mt, subtype: st, parameters: params, q: qvalue })
}
}
}
///|
fn acc_parse_media_range(
input : String,
) -> Result[(String, String), AcceptError] {
let s = acc_trim_string(input)
if s == "*/*" {
return Ok(("*", "*"))
}
let slash_pos = acc_find_char(s, 47) // /
match slash_pos {
None => return Err(InvalidMediaRange)
Some(pos) => {
let mt = acc_trim_string(acc_string_slice(s, 0, pos))
let st = acc_trim_string(acc_string_slice_from(s, pos + 1))
if mt == "*" {
if st != "*" {
return Err(InvalidMediaRange)
}
return Ok(("*", "*"))
}
if st == "*" {
if !acc_is_valid_token(mt) {
return Err(InvalidMediaRange)
}
return Ok((acc_to_lower_case(mt), "*"))
}
if !acc_is_valid_token(mt) || !acc_is_valid_token(st) {
return Err(InvalidMediaRange)
}
Ok((acc_to_lower_case(mt), acc_to_lower_case(st)))
}
}
}
///|
type WeightedToken = (String, QValue)
///|
fn acc_parse_weighted_tokens(
input : String,
lowercase : Bool,
allow_wildcard : Bool,
) -> Result[Array[WeightedToken], AcceptError] {
acc_parse_weighted_tokens_with_validator(
input, lowercase, allow_wildcard, acc_validate_token_or_star,
)
}
///|
fn acc_parse_weighted_tokens_with_validator(
input : String,
lowercase : Bool,
allow_wildcard : Bool,
validator : (String) -> Bool,
) -> Result[Array[WeightedToken], AcceptError] {
let s = acc_trim_string(input)
if s.length() == 0 {
return Err(Empty)
}
let parts = acc_split_with_quotes(s, 44) // ,
let mut items : Array[WeightedToken] = []
let mut part_idx = 0
while part_idx < parts.length() {
let part = acc_trim_string(parts[part_idx])
if part.length() > 0 {
let subparts = acc_split_with_quotes(part, 59) // ;
if subparts.length() == 0 {
return Err(InvalidFormat)
}
let token = acc_trim_string(subparts[0])
if token.length() == 0 {
return Err(InvalidFormat)
}
if token == "*" && !allow_wildcard {
return Err(InvalidFormat)
}
if token != "*" && !validator(token) {
return Err(InvalidToken)
}
let mut qvalue = { value: 1000 }
let mut q_seen = false
let mut sub_idx = 1
while sub_idx < subparts.length() {
let param = acc_trim_string(subparts[sub_idx])
if param.length() > 0 {
let eq_pos = acc_find_char(param, 61) // =
match eq_pos {
Some(pos) => {
let name = acc_trim_string(acc_string_slice(param, 0, pos))
if acc_to_lower_case(name) == "q" {
if q_seen {
return Err(InvalidQValue)
}
let value = acc_trim_string(
acc_string_slice_from(param, pos + 1),
)
let q_result = QValue::parse(value)
match q_result {
Ok(q) => {
qvalue = q
q_seen = true
}
Err(e) => return Err(e)
}
} else {
return Err(InvalidParameter)
}
}
None => {
let _ = ()
}
}
}
sub_idx = sub_idx + 1
}
let token_value = if lowercase && token != "*" {
acc_to_lower_case(token)
} else {
token
}
let new_items = []
let mut idx = 0
while idx < items.length() {
new_items.push(items[idx])
idx = idx + 1
}
new_items.push((token_value, qvalue))
items = new_items
}
part_idx = part_idx + 1
}
if items.length() == 0 {
return Err(Empty)
}
Ok(items)
}
///|
fn acc_parse_param_value(input : String) -> Result[String, AcceptError] {
let s = acc_trim_string(input)
if s.length() > 0 && acc_char_at(s, 0) == 34 { // "
let after_quote = acc_string_slice_from(s, 1)
let parse_result = acc_parse_quoted_string(after_quote)
match parse_result {
Ok((value, remaining)) => {
let rest = acc_trim_string(remaining)
if rest.length() != 0 {
return Err(InvalidParameter)
}
Ok(value)
}
Err(e) => Err(e)
}
} else {
if !acc_is_valid_token(s) {
return Err(InvalidToken)
}
Ok(s)
}
}
///|
fn acc_parse_quoted_string(
input : String,
) -> Result[(String, String), AcceptError] {
let mut result = ""
let mut escaped = false
let mut idx = 0
let len = input.length()
while idx < len {
let ch = acc_char_at(input, idx)
if escaped {
result = result + Int::unsafe_to_char(ch).to_string()
escaped = false
idx = idx + 1
} else if ch == 92 { // \
escaped = true
idx = idx + 1
} else if ch == 34 { // "
return Ok((result, acc_string_slice_from(input, idx + 1)))
} else {
result = result + Int::unsafe_to_char(ch).to_string()
idx = idx + 1
}
}
Err(InvalidParameter)
}
///|
fn acc_validate_token_or_star(token : String) -> Bool {
if token == "*" {
return true
}
acc_is_valid_token(token)
}
///|
fn acc_validate_language_range(token : String) -> Bool {
if token == "*" {
return true
}
acc_is_valid_language_tag(token)
}
///|
fn acc_is_valid_language_tag(tag : String) -> Bool {
if tag.length() == 0 {
return false
}
let parts = acc_split_string(tag, "-")
let mut idx = 0
while idx < parts.length() {
let part = parts[idx]
if part.length() == 0 || part.length() > 8 {
return false
}
let mut ch_idx = 0
while ch_idx < part.length() {
let ch = acc_char_at(part, ch_idx)
if !acc_is_alnum(ch) {
return false
}
ch_idx = ch_idx + 1
}
idx = idx + 1
}
true
}
///|
fn acc_is_valid_token(s : String) -> Bool {
if s.length() == 0 {
return false
}
let mut idx = 0
while idx < s.length() {
if !acc_is_token_char(acc_char_at(s, idx)) {
return false
}
idx = idx + 1
}
true
}
///|
fn acc_is_token_char(b : Int) -> Bool {
b == 33 ||
b == 35 ||
b == 36 ||
b == 37 ||
b == 38 ||
b == 39 ||
b == 42 ||
b == 43 ||
b == 45 ||
b == 46 ||
(b >= 48 && b <= 57) || // 0-9
(b >= 65 && b <= 90) || // A-Z
b == 94 ||
b == 95 ||
b == 96 ||
(b >= 97 && b <= 122) || // a-z
b == 124 ||
b == 126
}
///|
fn acc_is_alnum(b : Int) -> Bool {
(b >= 48 && b <= 57) || // 0-9
(b >= 65 && b <= 90) || // A-Z
(b >= 97 && b <= 122) // a-z
}
///|
fn acc_needs_quoting(s : String) -> Bool {
let mut idx = 0
while idx < s.length() {
if !acc_is_token_char(acc_char_at(s, idx)) {
return true
}
idx = idx + 1
}
false
}
///|
fn acc_escape_quotes(s : String) -> String {
let mut result = ""
let mut idx = 0
while idx < s.length() {
let ch = acc_char_at(s, idx)
if ch == 92 { // \
result = result + "\\\\"
} else if ch == 34 { // "
result = result + "\\\""
} else {
result = result + Int::unsafe_to_char(ch).to_string()
}
idx = idx + 1
}
result
}
///|
fn acc_to_lower_case(s : String) -> String {
str_to_lower_case(s)
}
///|
fn acc_trim_string(s : String) -> String {
str_trim_string(s)
}
///|
fn acc_trim_trailing_zeros(s : String) -> String {
let len = s.length()
if len > 0 && acc_char_at(s, len - 1) == 48 { // '0'
acc_string_slice(s, 0, len - 1)
} else {
s
}
}
///|
fn acc_char_at(s : String, idx : Int) -> Int {
str_char_at(s, idx)
}
///|
fn acc_find_char(s : String, target : Int) -> Int? {
let mut idx = 0
while idx < s.length() {
if acc_char_at(s, idx) == target {
return Some(idx)
}
idx = idx + 1
}
None
}
///|
fn acc_string_slice(s : String, start : Int, end : Int) -> String {
str_string_slice(s, start, end)
}
///|
fn acc_string_slice_from(s : String, start : Int) -> String {
str_string_slice_from(s, start)
}
///|
fn acc_string_starts_with(s : String, prefix : String) -> Bool {
if prefix.length() > s.length() {
return false
}
let mut idx = 0
while idx < prefix.length() {
if s[idx] != prefix[idx] {
return false
}
idx = idx + 1
}
true
}
///|
fn acc_string_ends_with(s : String, suffix : String) -> Bool {
if suffix.length() > s.length() {
return false
}
let s_len = s.length()
let suffix_len = suffix.length()
let mut idx = 0
while idx < suffix_len {
if s[s_len - suffix_len + idx] != suffix[idx] {
return false
}
idx = idx + 1
}
true
}
///|
fn acc_split_string(s : String, sep : String) -> Array[String] {
str_split_string(s, sep)
}
///|
fn acc_split_with_quotes(input : String, delimiter : Int) -> Array[String] {
let result : Array[String] = []
let mut start = 0
let mut in_quote = false
let mut escaped = false
let mut idx = 0
let len = input.length()
while idx < len {
let ch = acc_char_at(input, idx)
if escaped {
escaped = false
idx = idx + 1
continue
}
if ch == 92 && in_quote { // \
escaped = true
idx = idx + 1
continue
}
if ch == 34 { // "
in_quote = !in_quote
idx = idx + 1
continue
}
if ch == delimiter && !in_quote {
result.push(acc_string_slice(input, start, idx))
start = idx + 1
}
idx = idx + 1
}
result.push(acc_string_slice_from(input, start))
result
}
///|
fn[T, U] acc_map_items(arr : Array[T], f : (T) -> U) -> Array[U] {
let result : Array[U] = []
let mut idx = 0
while idx < arr.length() {
result.push(f(arr[idx]))
idx = idx + 1
}
result
}
///|
fn acc_join_strings(strs : Array[String], sep : String) -> String {
let mut result = ""
let mut idx = 0
while idx < strs.length() {
if idx > 0 {
result = result + sep
}
result = result + strs[idx]
idx = idx + 1
}
result
}
///|
fn acc_int_pow(base : Int, exp : Int) -> Int {
let mut result = 1
let mut idx = 0
while idx < exp {
result = result * base
idx = idx + 1
}
result
}