src/Value.bend
ber-core-store@0.1.2.0 · 90 lines · 0 definitions
1import Base
2import bend-kit-json@0.3.0.0/json.bend as Json
3import ber-core-store@0.1.2.0/src/JsonAdapter.bend as JsonAdapter
4import bend-codec-lib@0.2.0.0/bytes.bend as ByteLib
5import bend-codec-lib@0.2.0.0/hex.bend as HexLib
6
7# Multi-type stored values. Canonical forms are single-key objects so every
8# constructor hashes differently: {"t": s}, {"o": doc}, {"b": hex}. Binary
9# blobs hex-encode through the pinned codec and stay generic binary data,
10# never format-specific. Kit is named for the Val TYPE only; all JSON work
11# goes through JsonAdapter.
12
13# One stored record value: text, structured document, or binary blob.
14type Value is Data:
15 Text{content: String}
16 Object{object: Json.Val}
17 Blob{bytes: List<&2, U32>}
18
19# Canonical JSON of a value; the hashed bytes. Distinct keys per
20# constructor keep different values from sharing an address.
21def value_to_json(value: Value) -> Json.Val:
22 match value:
23 case Text{content}:
24 JsonAdapter.make_obj(Con{JsonAdapter.make_kv("t", JsonAdapter.make_str(content)), Nil{}})
25 case Object{document}:
26 JsonAdapter.make_obj(Con{JsonAdapter.make_kv("o", document), Nil{}})
27 case Blob{byte_list}:
28 JsonAdapter.make_obj(Con{JsonAdapter.make_kv("b", JsonAdapter.make_str(HexLib.Hex.encode(ByteLib.Bytes{byte_list}, HexLib.HexLower{}))), Nil{}})
29
30# Canonical text of a value.
31def value_to_canonical(value: Value) -> String:
32 JsonAdapter.encode_canonical(value_to_json(value))
33
34# Reads the "t" field as text.
35def text_string_or_nothing(text_maybe: Maybe<&2, Json.Val>) -> Maybe<&2, String>:
36 match text_maybe:
37 case None{}:
38 None{}
39 case Some{text_val}:
40 JsonAdapter.as_str(text_val)
41
42# Unwraps the decoded byte box.
43def bytes_data(byte_box: ByteLib.Bytes) -> List<&2, U32>:
44 match byte_box:
45 case ByteLib.Bytes{raw_data}:
46 raw_data
47
48# Reads a hex decode result as a blob; malformed hex reads as None.
49def blob_or_nothing(decode_result: Result<&2, &2, HexLib.Hex.Error, ByteLib.Bytes>) -> Maybe<&2, Value>:
50 match decode_result:
51 case Fail{decode_error}:
52 None{}
53 case Done{byte_box}:
54 Some{Blob{bytes_data(byte_box)}}
55
56# Reads the "b" field as a blob.
57def blob_from_hex(hex_maybe: Maybe<&2, String>) -> Maybe<&2, Value>:
58 match hex_maybe:
59 case None{}:
60 None{}
61 case Some{hex_text}:
62 blob_or_nothing(HexLib.Hex.decode(hex_text))
63
64# Reads the hex text behind the "b" field.
65def hex_text_or_nothing(hex_maybe: Maybe<&2, Json.Val>) -> Maybe<&2, String>:
66 match hex_maybe:
67 case None{}:
68 None{}
69 case Some{hex_val}:
70 JsonAdapter.as_str(hex_val)
71
72# Falls through from text to object to blob; unknown shapes read as None.
73def from_object_value(object_maybe: Maybe<&2, Json.Val>, hex_maybe: Maybe<&2, Json.Val>) -> Maybe<&2, Value>:
74 match object_maybe:
75 case None{}:
76 blob_from_hex(hex_text_or_nothing(hex_maybe))
77 case Some{document}:
78 Some{Object{document}}
79
80# Falls through from text to the remaining shapes.
81def from_text_string(text_maybe: Maybe<&2, String>, +document: Json.Val) -> Maybe<&2, Value>:
82 match text_maybe:
83 case None{}:
84 from_object_value(JsonAdapter.get_field(document, "o"), JsonAdapter.get_field(document, "b"))
85 case Some{content}:
86 Some{Text{content}}
87
88# Parses canonical value JSON; unknown shapes read as None.
89def value_from_json(+document: Json.Val) -> Maybe<&2, Value>:
90 from_text_string(text_string_or_nothing(JsonAdapter.get_field(document, "t")), document)