///|
/// An optional string as the wire carries it: the value, or `null`.
///
/// Not the field's own `ToJson` — MoonBit encodes `Some(v)` as the one-element
/// array `[v]`, which a string lookup rejects, silently turning a present
/// field into an absent one.
fn or_null(value : String?) -> Json {
match value {
Some(value) => Json::string(value)
None => Json::null()
}
}
///|
/// An array of strings as a JSON array.
fn strings_to_json(values : Array[String]) -> Json {
Json::array(values.map(Json::string))
}
///|
/// A required string field. `None` when absent or not a string.
fn text(fields : Map[String, Json], key : String) -> String? {
match fields.get(key) {
Some(String(value)) => Some(value)
_ => None
}
}
///|
/// An optional string field: present-and-string, or absent/null.
///
/// Distinct from `text` in that a `null` is a successful read of "no value"
/// rather than a malformed line, so callers cannot conflate the two.
fn opt_text(fields : Map[String, Json], key : String) -> String? {
match fields.get(key) {
Some(String(value)) => Some(value)
_ => None
}
}
///|
/// A required integer field. JSON has one number type, so a whole-valued
/// double is accepted and anything fractional is not.
fn int(fields : Map[String, Json], key : String) -> Int32? {
match fields.get(key) {
Some(Number(value, ..)) => {
let truncated = value.to_int()
if truncated.to_double() == value {
Some(truncated)
} else {
None
}
}
_ => None
}
}
///|
/// A required array-of-strings field. Absent reads as empty: every producer of
/// these lists writes them, and an empty list and a missing one mean the same
/// thing to every renderer.
fn strings(fields : Map[String, Json], key : String) -> Array[String] {
match fields.get(key) {
Some(Array(items)) => {
let out : Array[String] = []
for item in items {
if item is String(value) {
out.push(value)
}
}
out
}
_ => []
}
}
///|
/// A required nested object field.
fn object(fields : Map[String, Json], key : String) -> Map[String, Json]? {
match fields.get(key) {
Some(Object(nested)) => Some(nested)
_ => None
}
}