treevault/src/json_serde.gleam
Ivan Yuriev ad9ef045cb
Some checks failed
test / test (push) Has been cancelled
updated dependencies
2025-02-17 23:58:23 +03:00

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))
}