///|
/// Text and numeric utilities shared by the parser, writer and CLI.
fn byte_length(input : String) -> Int {
@utf8.encode(input).length()
}
///|
fn strip_one_cr(line : String) -> String {
if line.has_suffix("\r") {
let chars = line.to_array()
String::from_array(chars[0:chars.length() - 1])
} else {
line
}
}
///|
fn contains_illegal_control(input : String) -> Bool {
for ch in input {
let value = ch.to_int()
if (value >= 0 && value < 32 && ch != '\r' && ch != '\n' && ch != '\t') ||
value == 127 {
return true
}
}
false
}
///|
fn is_ascii_alpha(ch : Char) -> Bool {
let value = ch.to_int()
(value >= 'A'.to_int() && value <= 'Z'.to_int()) ||
(value >= 'a'.to_int() && value <= 'z'.to_int())
}
///|
fn is_ascii_digit(ch : Char) -> Bool {
let value = ch.to_int()
value >= '0'.to_int() && value <= '9'.to_int()
}
///|
fn is_token_char(ch : Char) -> Bool {
let value = ch.to_int()
value >= 33 && value <= 126 && ch != ' ' && ch != '=' && ch != ':'
}
///|
fn valid_token(value : String) -> Bool {
if value.length() == 0 {
return false
}
for ch in value {
if !is_token_char(ch) {
return false
}
}
true
}
///|
fn valid_attribute_name(value : String) -> Bool {
if value.length() == 0 {
return false
}
for ch in value {
if !(is_ascii_alpha(ch) || is_ascii_digit(ch) || ch == '-' || ch == '_') {
return false
}
}
true
}
///|
fn parse_non_negative_int(value : String) -> Int? {
if value.length() == 0 {
return None
}
let mut out = 0
for ch in value {
if !is_ascii_digit(ch) {
return None
}
let digit = ch.to_int() - '0'.to_int()
if out > 214748364 || (out == 214748364 && digit > 7) {
return None
}
out = out * 10 + digit
}
Some(out)
}
///|
fn split_compact(value : String, separator : String) -> Array[String] {
let out : Array[String] = Array::new()
for item in value.split(separator) {
let owned = item.to_owned()
if owned.length() > 0 {
out.push(owned)
}
}
out
}
///|
fn join_with(items : Array[String], separator : String) -> String {
let sb = StringBuilder()
for index, item in items {
if index > 0 {
sb.write_string(separator)
}
sb.write_string(item)
}
sb.to_string()
}
///|
fn ascii_lower(value : String) -> String {
let chars : Array[Char] = Array::new()
for ch in value {
let code = ch.to_int()
if code >= 'A'.to_int() && code <= 'Z'.to_int() {
chars.push((code + 32).unsafe_to_char())
} else {
chars.push(ch)
}
}
String::from_array(chars)
}
///|
fn string_in(items : Array[String], value : String) -> Bool {
for item in items {
if item == value {
return true
}
}
false
}
///|
fn count_attribute_named(items : Array[Attribute], name : String) -> Int {
let mut count = 0
for item in items {
if item.name().equal_ignore_ascii_case(name) {
count = count + 1
}
}
count
}
///|
fn json_escape(value : String) -> String {
let sb = StringBuilder()
for ch in value {
match ch {
'"' => sb.write_string("\\\"")
'\\' => sb.write_string("\\\\")
'\n' => sb.write_string("\\n")
'\r' => sb.write_string("\\r")
'\t' => sb.write_string("\\t")
_ => if ch.to_int() >= 32 { sb.write_char(ch) }
}
}
sb.to_string()
}