src/Value.bend source
src/Value.bend on the hub · documented module
import Baseimport bend-kit-json@0.3.0.0/json.bend as Jsonimport ./JsonAdapter.bend as JsonAdapterimport bend-codec-lib@0.2.0.0/bytes.bend as ByteLibimport bend-codec-lib@0.2.0.0/hex.bend as HexLib# Multi-type stored values. Canonical forms are single-key objects so every# constructor hashes differently: {"t": s}, {"o": doc}, {"b": hex}. Binary# blobs hex-encode through the pinned codec and stay generic binary data,# never format-specific. Kit is named for the Val TYPE only; all JSON work# goes through JsonAdapter.# One stored record value: text, structured document, or binary blob.type Value is Data: Text{content: String} Object{object: Json.Val} Blob{bytes: List<&2, U32>}# Canonical JSON of a value; the hashed bytes. Distinct keys per# constructor keep different values from sharing an address.def value_to_json(value: Value) -> Json.Val: match value: case Text{content}: JsonAdapter.make_obj(Con{JsonAdapter.make_kv("t", JsonAdapter.make_str(content)), Nil{}}) case Object{document}: JsonAdapter.make_obj(Con{JsonAdapter.make_kv("o", document), Nil{}}) case Blob{byte_list}: JsonAdapter.make_obj(Con{JsonAdapter.make_kv("b", JsonAdapter.make_str(HexLib.Hex.encode(ByteLib.Bytes{byte_list}, HexLib.HexLower{}))), Nil{}})# Canonical text of a value.def value_to_canonical(value: Value) -> String: JsonAdapter.encode_canonical(value_to_json(value))# Reads the "t" field as text.def text_string_or_nothing(text_maybe: Maybe<&2, Json.Val>) -> Maybe<&2, String>: match text_maybe: case None{}: None{} case Some{text_val}: JsonAdapter.as_str(text_val)# Unwraps the decoded byte box.def bytes_data(byte_box: ByteLib.Bytes) -> List<&2, U32>: match byte_box: case ByteLib.Bytes{raw_data}: raw_data# Reads a hex decode result as a blob; malformed hex reads as None.def blob_or_nothing(decode_result: Result<&2, &2, HexLib.Hex.Error, ByteLib.Bytes>) -> Maybe<&2, Value>: match decode_result: case Fail{decode_error}: None{} case Done{byte_box}: Some{Blob{bytes_data(byte_box)}}# Reads the "b" field as a blob.def blob_from_hex(hex_maybe: Maybe<&2, String>) -> Maybe<&2, Value>: match hex_maybe: case None{}: None{} case Some{hex_text}: blob_or_nothing(HexLib.Hex.decode(hex_text))# Reads the hex text behind the "b" field.def hex_text_or_nothing(hex_maybe: Maybe<&2, Json.Val>) -> Maybe<&2, String>: match hex_maybe: case None{}: None{} case Some{hex_val}: JsonAdapter.as_str(hex_val)# Falls through from text to object to blob; unknown shapes read as None.def from_object_value(object_maybe: Maybe<&2, Json.Val>, hex_maybe: Maybe<&2, Json.Val>) -> Maybe<&2, Value>: match object_maybe: case None{}: blob_from_hex(hex_text_or_nothing(hex_maybe)) case Some{document}: Some{Object{document}}# Falls through from text to the remaining shapes.def from_text_string(text_maybe: Maybe<&2, String>, +document: Json.Val) -> Maybe<&2, Value>: match text_maybe: case None{}: from_object_value(JsonAdapter.get_field(document, "o"), JsonAdapter.get_field(document, "b")) case Some{content}: Some{Text{content}}# Parses canonical value JSON; unknown shapes read as None.def value_from_json(+document: Json.Val) -> Maybe<&2, Value>: from_text_string(text_string_or_nothing(JsonAdapter.get_field(document, "t")), document)