///|
fn valid_key(key : String) -> Bool {
if key.length() == 0 || !is_key_start(key.at(0)) {
return false
}
let mut index = 1
while index < key.length() {
if !is_key_char(key.at(index)) {
return false
}
index = index + 1
}
true
}
///|
fn valid_token(token : String) -> Bool {
if token.length() == 0 {
return false
}
let first = token.at(0)
if !is_ascii_alpha(first) && first != (42 : UInt16) {
return false
}
let mut index = 1
while index < token.length() {
if !is_token_char(token.at(index)) {
return false
}
index = index + 1
}
true
}
///|
fn serialize_string_value(value : String) -> Result[String, SerializeError] {
let builder = StringBuilder::StringBuilder(size_hint=value.length() + 2)
builder.write_string("\"")
let mut index = 0
while index < value.length() {
let ch = value.at(index)
if ch < (32 : UInt16) || ch > (126 : UInt16) {
return Err(InvalidString(value))
}
if ch == (34 : UInt16) || ch == (92 : UInt16) {
builder.write_string("\\")
}
builder.write_substring(value, index, 1)
index = index + 1
}
builder.write_string("\"")
Ok(builder.to_string())
}
///|
fn serialize_decimal_value(value : Double) -> Result[String, SerializeError] {
if value < -999_999_999_999.999 || value > 999_999_999_999.999 {
return Err(DecimalOutOfRange(value))
}
let is_neg = value < 0.0
let abs_val = if is_neg { -value } else { value }
let scaled_floor = (abs_val * 1000.0).to_int64()
let diff = abs_val * 1000.0 - scaled_floor.to_double()
let scaled = if diff > 0.5000000001 ||
(diff >= 0.4999999999 && scaled_floor % 2L != 0L) {
scaled_floor + 1L
} else {
scaled_floor
}
let int_part = scaled / 1000L
let frac_part = (scaled % 1000L).to_int()
let builder = StringBuilder::StringBuilder()
if is_neg && (int_part != 0L || frac_part != 0) {
builder.write_string("-")
}
builder.write_string(int_part.to_string())
builder.write_string(".")
if frac_part == 0 {
builder.write_string("0")
} else if frac_part % 100 == 0 {
builder.write_string((frac_part / 100).to_string())
} else if frac_part % 10 == 0 {
let d1 = frac_part / 100
let d2 = frac_part / 10 % 10
builder.write_string(d1.to_string())
builder.write_string(d2.to_string())
} else {
let d1 = frac_part / 100
let d2 = frac_part / 10 % 10
let d3 = frac_part % 10
builder.write_string(d1.to_string())
builder.write_string(d2.to_string())
builder.write_string(d3.to_string())
}
Ok(builder.to_string())
}
///|
fn hex_char_lower(val : Int) -> String {
let table = "0123456789abcdef"
let builder = StringBuilder::StringBuilder()
builder.write_substring(table, val & 0xF, 1)
builder.to_string()
}
///|
fn serialize_display_string_value(
value : String,
) -> Result[String, SerializeError] {
let bytes = string_to_utf8_bytes(value)
let builder = StringBuilder::StringBuilder()
builder.write_string("%\"")
let mut i = 0
while i < bytes.length() {
let b = bytes[i].to_int()
if b == 0x25 || b == 0x22 || b <= 0x1F || b >= 0x7F {
builder.write_string("%")
builder.write_string(hex_char_lower((b >> 4) & 0xF))
builder.write_string(hex_char_lower(b & 0xF))
} else {
let ch = match b.to_char() {
Some(c) => c
None => return Err(InvalidDisplayString(value))
}
builder.write_char(ch)
}
i = i + 1
}
builder.write_string("\"")
Ok(builder.to_string())
}
///|
fn serialize_bare_item(value : BareItem) -> Result[String, SerializeError] {
match value {
SfBoolean(true) => Ok("?1")
SfBoolean(false) => Ok("?0")
SfInteger(integer) =>
if integer < -999_999_999_999_999L || integer > 999_999_999_999_999L {
Err(IntegerOutOfRange(integer))
} else {
Ok(integer.to_string())
}
SfDecimal(dec) => serialize_decimal_value(dec)
SfString(string) => serialize_string_value(string)
SfToken(token) =>
if valid_token(token) {
Ok(token)
} else {
Err(InvalidToken(token))
}
SfByteSequence(bytes) => {
let b64 = base64_encode(bytes)
Ok(":" + b64 + ":")
}
SfDate(date) =>
if date < -999_999_999_999_999L || date > 999_999_999_999_999L {
Err(DateOutOfRange(date))
} else {
Ok("@" + date.to_string())
}
SfDisplayString(dstr) => serialize_display_string_value(dstr)
}
}
///|
fn serialize_parameters_into(
builder : StringBuilder,
parameters : Array[Parameter],
) -> Result[Unit, SerializeError] {
let mut index = 0
while index < parameters.length() {
let parameter = parameters[index]
if !valid_key(parameter.key) {
return Err(InvalidParameterKey(parameter.key))
}
builder.write_string(";")
builder.write_string(parameter.key)
match parameter.value {
SfBoolean(true) => ()
_ => {
builder.write_string("=")
match serialize_bare_item(parameter.value) {
Ok(serialized) => builder.write_string(serialized)
Err(error) => return Err(error)
}
}
}
index = index + 1
}
Ok(())
}
///|
/// Serialize an RFC 9651 Item into its canonical textual representation.
pub fn serialize_item(item : Item) -> Result[String, SerializeError] {
let builder = StringBuilder::StringBuilder()
match serialize_bare_item(item.value) {
Ok(serialized) => builder.write_string(serialized)
Err(error) => return Err(error)
}
match serialize_parameters_into(builder, item.parameters) {
Ok(_) => Ok(builder.to_string())
Err(err) => Err(err)
}
}
///|
fn serialize_inner_list_into(
builder : StringBuilder,
inner_list : InnerList,
) -> Result[Unit, SerializeError] {
builder.write_string("(")
let mut i = 0
while i < inner_list.items.length() {
if i > 0 {
builder.write_string(" ")
}
let serialized = match serialize_item(inner_list.items[i]) {
Ok(s) => s
Err(err) => return Err(err)
}
builder.write_string(serialized)
i = i + 1
}
builder.write_string(")")
serialize_parameters_into(builder, inner_list.parameters)
}
///|
fn serialize_list_member_into(
builder : StringBuilder,
member_val : ListMember,
) -> Result[Unit, SerializeError] {
match member_val {
Item(item) => {
let serialized = match serialize_item(item) {
Ok(s) => s
Err(err) => return Err(err)
}
builder.write_string(serialized)
Ok(())
}
InnerList(il) => serialize_inner_list_into(builder, il)
}
}
///|
/// Serialize an RFC 9651 List into its canonical textual representation.
pub fn serialize_list(
list : Array[ListMember],
) -> Result[String, SerializeError] {
let builder = StringBuilder::StringBuilder()
let mut i = 0
while i < list.length() {
if i > 0 {
builder.write_string(", ")
}
match serialize_list_member_into(builder, list[i]) {
Ok(_) => ()
Err(err) => return Err(err)
}
i = i + 1
}
Ok(builder.to_string())
}
///|
/// Serialize an RFC 9651 Dictionary into its canonical textual representation.
pub fn serialize_dictionary(
dict : Array[DictionaryMember],
) -> Result[String, SerializeError] {
let builder = StringBuilder::StringBuilder()
let mut i = 0
while i < dict.length() {
if i > 0 {
builder.write_string(", ")
}
let entry = dict[i]
if !valid_key(entry.key) {
return Err(InvalidDictionaryKey(entry.key))
}
builder.write_string(entry.key)
match entry.value {
Item(item) if item.value == SfBoolean(true) =>
match serialize_parameters_into(builder, item.parameters) {
Ok(_) => ()
Err(err) => return Err(err)
}
_ => {
builder.write_string("=")
match serialize_list_member_into(builder, entry.value) {
Ok(_) => ()
Err(err) => return Err(err)
}
}
}
i = i + 1
}
Ok(builder.to_string())
}