///|
pub(all) enum Value {
Null
Bool(Bool)
Number(Double)
String(String)
Array(Array[Value])
Object(Map[String, Value])
} derive(Eq, Debug)
///|
pub extend Value with @moonbitlang/core/debug.Debug::{to_repr}
///|
pub extend Value with Eq::{not_equal, equal}
///|
pub fn from_json(value : Json) -> Value {
match value {
Json::Null => Null
Json::True => Bool(true)
Json::False => Bool(false)
Json::Number(n, ..) => Number(n)
Json::String(s) => String(s)
Json::Array(items) => Array(items.map(from_json))
Json::Object(fields) => Object(fields.map(fn(_, v) { from_json(v) }))
}
}
///|
pub fn Value::to_json(self : Value) -> Json {
match self {
Null => Json::null()
Bool(b) => Json::boolean(b)
Number(n) => Json::number(n)
String(s) => Json::string(s)
Array(items) => Json::array(items.map(fn(v) { v.to_json() }))
Object(fields) => Json::object(fields.map(fn(_, v) { v.to_json() }))
}
}
///|
pub fn Value::stringify(self : Value) -> String {
self.to_json().stringify()
}
///|
fn parse_value(source : StringView) -> Value raise {
from_json(@json.parse(source))
}
///|
pub fn execute(request : Json) -> Json raise {
run(from_json(request)).to_json()
}
///|
/// Maps every leaf (a scalar or an empty container) to its JSON Pointer
/// path. The root path is "" and keys are escaped as in RFC 6901.
pub fn Value::flatten(self : Value) -> Map[String, Value] {
let result : Map[String, Value] = Map([])
fn walk(value : Value, path : String) -> Unit {
match value {
Object(fields) if !fields.is_empty() =>
for key, child in fields {
walk(child, path + "/" + escape_key(key))
}
Array(items) if !items.is_empty() =>
for i, item in items {
walk(item, path + "/" + i.to_string())
}
leaf => result[path] = leaf
}
}
walk(self, "")
result
}
///|
/// Returns the value at a JSON Pointer path, raising an input error when
/// the path is malformed or absent.
pub fn Value::pointer(self : Value, path : String) -> Value raise {
at(self, path)
}
///|
/// Canonical JSON text: object keys in UTF-16 code unit order, no
/// whitespace, arrays in order.
pub fn Value::canonical(self : Value) -> String {
canonical(self)
}
///|
fn escape_key(key : String) -> String {
key.replace_all(old="~", new="~0").replace_all(old="/", new="~1")
}
///|
/// Walks a JSON Pointer (RFC 6901); absent or malformed paths raise.
fn at(root : Value, pointer : String) -> Value raise {
if pointer != "" && !pointer.has_prefix("/") {
raise input_error("E3002", "pointer must start with /")
}
let mut current = root
let parts : Array[String] = []
if pointer != "" {
let raw = pointer.split("/").collect()
ignore(raw.remove(0))
for view in raw {
parts.push(
view
.to_owned()
.replace_all(old="~1", new="/")
.replace_all(old="~0", new="~"),
)
}
}
for part in parts {
match current {
Object(fields) =>
current = match fields.get(part) {
Some(v) => v
None =>
raise input_error("E3002", "missing pointer target: " + pointer)
}
Array(items) => {
let index = match num(parse_value(part)) {
n if n >= 0.0 && n.floor() == n && n < items.length().to_double() => n
_ => raise input_error("E3002", "array pointer out of range")
}
current = items[index.to_int()]
}
_ => raise input_error("E3002", "pointer traverses scalar")
}
}
current
}