//// Functions to allow serialization from Tree to Json and back! import gleam/dict import gleam/dynamic import gleam/dynamic/decode import gleam/json.{bool, float, int, null, object, string} import gleam/list import gleam/result import tree pub type JsonCodecError { EncodingError DecodingError } pub fn serialize(tree) { tree |> tree_to_json |> json.to_string } fn tree_to_json(tree: tree.Tree) -> json.Json { case tree { tree.Leaf(_) as lf -> leaf_to_json(lf) tree.Node(children) | tree.Root(children) -> { children |> dict.to_list |> list.map(fn(pair) { let #(k, v) = pair #(k, v |> tree_to_json) }) |> object() } } } fn leaf_to_json(tree: tree.Tree) -> json.Json { case tree { tree.Leaf(data) -> case data { tree.Bool(b) -> bool(b) tree.Float(f) -> float(f) tree.Int(i) -> int(i) tree.Null -> null() tree.String(s) -> string(s) } _ -> panic as "Tried to serialize not a Leaf." } } /// Decodes a full tree (which root is tree.Root) from JSON pub fn deserialize(string) { case json.parse(string, decode.dynamic) { Error(_) -> Error(DecodingError) Ok(data) -> data |> tree_decoder(True) |> Ok } } /// Decodes a subtree (which root is NOT tree.Root) from JSON pub fn deserialize_sub(string) { case json.parse(string, decode.dynamic) { Error(_) -> Error(DecodingError) Ok(data) -> data |> tree_decoder(False) |> Ok } } fn tree_decoder(data, root) { let _ = case dynamic.classify(data), root { "Dict", True -> { let children = decode.dict(decode.string, decode.dynamic) |> decode.run(data, _) case children { Error(_) -> panic as "EncodingError" Ok(children) -> children |> dict.map_values(fn(_, v) { tree_decoder(v, False) }) |> tree.Root } } "Dict", False -> { let children = decode.dict(decode.string, decode.dynamic) |> decode.run(data, _) case children { Error(_) -> panic as "EncodingError" Ok(children) -> children |> dict.map_values(fn(_, v) { tree_decoder(v, False) }) } |> tree.Node } _, _ -> case leaf_decoder(data) { Error(_) -> panic as "EncodingError" Ok(leaf) -> leaf } } } pub fn decode_leafdata(data) { case dynamic.classify(data) { "Int" -> { use value <- result.try(decode.run(data, decode.int)) Ok(tree.Int(value)) } "Float" -> { use value <- result.try(decode.run(data, decode.float)) Ok(tree.Float(value)) } "Bool" -> { use value <- result.try(decode.run(data, decode.bool)) Ok(tree.Bool(value)) } "String" -> { use value <- result.try(decode.run(data, decode.string)) Ok(tree.String(value)) } _ -> Ok(tree.Null) } } fn leaf_decoder(data) { use data <- result.try(decode_leafdata(data)) Ok(tree.Leaf(data)) }