import gleam/dynamic.{type Dynamic}
import gleam/dynamic/decode.{type DecodeError}
import gleam/json
/// The discriminator property name used by JSON Tag.
pub const type_field = "#type"
/// Function signature for encoding a tagged JSON object.
pub type TagEncoder =
fn(String, List(#(String, json.Json))) -> json.Json
/// Encodes a tagged JSON object with `#type` set to the given tag.
///
/// ```gleam
/// encode(tag: "Circle", fields: [#("radius", json.float(4.0))])
/// |> json.to_string()
/// // {"#type":"Circle","radius":4.0}
/// ```
pub fn encode(
tag tag: String,
fields fields: List(#(String, json.Json)),
) -> json.Json {
json.object([#(type_field, json.string(tag)), ..fields])
}
/// Decodes a tagged JSON dynamic value, extracting the `#type` tag
/// and returning the tag value with the original dynamic for further
/// field decoding.
pub fn decode(
from json: Dynamic,
) -> Result(#(String, Dynamic), List(DecodeError)) {
let tag_decoder = {
use tag <- decode.field(type_field, decode.string)
decode.success(tag)
}
case decode.run(json, tag_decoder) {
Ok(tag) -> Ok(#(tag, json))
Error(errors) -> Error(errors)
}
}
/// Extracts just the `#type` tag value from a dynamic JSON value.
pub fn tag_of(from json: Dynamic) -> Result(String, List(DecodeError)) {
let tag_decoder = {
use tag <- decode.field(type_field, decode.string)
decode.success(tag)
}
decode.run(json, tag_decoder)
}