///|
/// Values are tagged so integer and decimal equality remains type preserving.
fn codec_value_to_json(value : Value) -> Json {
match value {
Text(text) => { "type": "text", "value": Json::string(text) }
Integer(number) =>
{ "type": "integer", "value": Json::string(number.to_string()) }
Decimal(number) => { "type": "decimal", "value": Json::number(number) }
Boolean(flag) => { "type": "boolean", "value": Json::boolean(flag) }
NullValue => { "type": "null" }
}
}
///|
pub fn value_to_json(value : Value) -> Json raise CepError {
value.validate()
codec_value_to_json(value)
}
///|
fn codec_value_from_json(value : Json, path : String) -> Value raise CepError {
let object = codec_object(value, path)
let tag = codec_string(codec_required(object, "type", path), "\{path}.type")
if tag == "null" {
codec_keys(object, ["type"], path)
return NullValue
}
if tag != "text" && tag != "integer" && tag != "decimal" && tag != "boolean" {
raise Invalid(path="\{path}.type", reason="unknown scalar type")
}
codec_keys(object, ["type", "value"], path)
let raw = codec_required(object, "value", path)
let value_path = "\{path}.value"
match tag {
"text" => {
let text = codec_string(raw, value_path)
if text.length() > 4096 {
raise Invalid(
path=value_path,
reason="text exceeds 4096 UTF-16 code units",
)
}
Text(text)
}
"integer" => Integer(codec_int64(raw, value_path))
"decimal" =>
match raw {
Number(number, ..) =>
if finite(number) {
Decimal(number)
} else {
raise Invalid(path=value_path, reason="decimal must be finite")
}
_ => raise Invalid(path=value_path, reason="expected JSON number")
}
"boolean" => Boolean(codec_boolean(raw, value_path))
_ => raise Invalid(path="\{path}.type", reason="unknown scalar type")
}
}
///|
pub fn value_from_json(value : Json) -> Value raise CepError {
codec_value_from_json(value, "value")
}