1 import Base 2 import bend-kit-json@0.3.0.0/json.bend as Json 3 4 # Sole owner of JSON operations. Consumers import THIS module for all 5 # encode/parse/get work; they import the kit package ONLY for the Val type in 6 # annotations (Bend cannot alias types across modules — same convention as 7 # MyLsmStore.Sess). 8 # TRUST BOUNDARY: Json.parse is structural recursion. Json.encode is @unsafe 9 # upstream (work-list walk, documented in kit source); canonical-bytes 10 # (equal content => identical text) is covered by json_bytes_probe and the 11 # differential check vs Python json, not by proof. If kit ships a safe 12 # encoder, only this file changes. 13 14 # Wraps text as a JSON string value. 15 def make_str(+text: String) -> Json.Val: 16 Json.Str{text} 17 18 # Wraps a value list as a JSON array. 19 def make_arr(items: List<&2, Json.Val>) -> Json.Val: 20 Json.Arr{items} 21 22 # Builds one object field pair. 23 def make_kv(+field_key: String, field_value: Json.Val) -> Sigma<&2, &2, String, _ => Json.Val>: 24 (field_key, field_value) 25 26 # Inserts one field pair into the map. 27 def fold_pair(field_map: Map<&2, Json.Val>, field_pair: Sigma<&2, &2, String, _ => Json.Val>) -> Map<&2, Json.Val>: 28 match field_pair: 29 case (field_key, field_value): 30 Map.set(&2, Json.Val, field_map, field_key, field_value) 31 32 # Folds a field list into a map; the list shrinks first. 33 def fold_fields(remaining_fields: List<&2, Sigma<&2, &2, String, _ => Json.Val>>, field_map: Map<&2, Json.Val>) -> Map<&2, Json.Val>: 34 match remaining_fields: 35 case Nil{}: 36 field_map 37 case Con{field_pair, tail_fields}: 38 fold_fields(tail_fields, fold_pair(field_map, field_pair)) 39 40 # Builds an object; keys come out sorted by construction. 41 def make_obj(fields: List<&2, Sigma<&2, &2, String, _ => Json.Val>>) -> Json.Val: 42 Json.Obj{fold_fields(fields, Map.new(&2, Json.Val))} 43 44 # Parses stored text; None on malformed input. 45 def parse_text(+stored_text: String) -> Maybe<&2, Json.Val>: 46 Json.parse(stored_text) 47 48 # Looks up an object field; None when absent or not an object. 49 def get_field(+document: Json.Val, +field_name: String) -> Maybe<&2, Json.Val>: 50 Json.get(document, field_name) 51 52 # Unwraps a string value; None otherwise. 53 def as_str(json_value: Json.Val) -> Maybe<&2, String>: 54 match json_value: 55 case Json.Str{text}: 56 Some{text} 57 case _: 58 None{} 59 60 # Unwraps an array value; None otherwise. 61 def as_arr(json_value: Json.Val) -> Maybe<&2, List<&2, Json.Val>>: 62 match json_value: 63 case Json.Arr{items}: 64 Some{items} 65 case _: 66 None{} 67 68 # Encodes with keys sorted; the canonical bytes. 69 def encode_canonical(+document: Json.Val) -> String: 70 Json.encode(document)