131 lines
2.9 KiB
Gleam
131 lines
2.9 KiB
Gleam
//// 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))
|
|
}
|