Current section

Files

Jump to
gserde src serializer.gleam
Raw

src/serializer.gleam

import glance
import internal/codegen/statements as gens
import internal/codegen/types as t
import gleam/string
import gleam/list
import gleam/option
import gleam/io
import gleam/int
import request.{Request}
import internal/path.{basename}
fn glance_t_to_codegen_t(x: glance.Type) -> t.GleamType {
case x {
glance.NamedType(name, _module, parameters) -> {
// @todo resolve types from modules
// io.debug(#("glance_t_to_codegen_t", name))
case name {
"List" -> {
let assert Ok(t0) = list.at(parameters, 0)
t.ListType(glance_t_to_codegen_t(t0))
}
"Option" -> {
// @todo options are untagged, and just `null | T`
// https://serde.rs/enum-representations.html
let assert Ok(t0) = list.at(parameters, 0)
t.option(glance_t_to_codegen_t(t0))
}
"Result" -> {
// @todo options are untagged, and just `null | T`
// https://serde.rs/enum-representations.html
panic as "Result is unimplemented! serde-style tagging support needed https://serde.rs/enum-representations.html"
}
_ -> t.AnonymousType(name)
}
}
glance.TupleType(elements) ->
t.TupleType(list.map(elements, glance_t_to_codegen_t))
glance.FunctionType(_paramters, _return) -> {
panic as "cannot serialize entities with functions"
}
glance.VariableType(_name) -> {
// io.debug(name)
panic as "unimplemented! VariableType"
}
}
}
pub fn get_json_serializer_str(ct: t.GleamType) {
case ct {
t.AnonymousType(name) -> "json." <> string.lowercase(name)
t.TupleType(_) -> {
"json.preprocessed_array"
}
t.ListType(_) -> "json.array"
t.VariantDepType(name, _, _) -> {
case name {
"Option" -> {
"json.nullable"
}
_ -> {
panic as "VariantDefType not supported: " <> name
}
}
}
_ -> {
// io.debug(#("codegen type failed: ", ct))
todo
}
}
}
fn codegen_t_to_codegen_json_t(ct, field_name) {
// io.debug(#("codegen type: ", ct))
let json_call_fn_str = get_json_serializer_str(ct)
let field_name_var = gens.VarPrimitive("t." <> field_name)
case ct {
t.AnonymousType(_) -> gens.call(json_call_fn_str, [field_name_var])
t.TupleType(els) -> {
gens.call(json_call_fn_str, [
gens.list(
list.index_map(els, fn(el, i) {
codegen_t_to_codegen_json_t(
el,
field_name <> "." <> int.to_string(i),
)
}),
),
])
}
t.ListType(inner) -> {
gens.call(json_call_fn_str, [
field_name_var,
gens.VarPrimitive(get_json_serializer_str(inner)),
])
}
t.VariantDepType(_name, dep_types, _variants) -> {
gens.call(json_call_fn_str, [
field_name_var,
..list.map(dep_types, fn(inner) {
gens.VarPrimitive(get_json_serializer_str(inner))
})
])
}
_ -> {
// io.debug(#("codegen_t_to_codegen_json_t failed: ", ct))
todo
}
}
}
// glance ast -> codegen ast of json serializers
fn serializer_of_t(x: glance.Type, field_name: String) {
let ct = glance_t_to_codegen_t(x)
codegen_t_to_codegen_json_t(ct, field_name)
}
fn gen_to_json(req) {
let Request(
src_module_name: src_module_name,
type_name: type_name,
variant: variant,
..,
) = req
gens.Function(
// string.lowercase(variant.name) <> "_to_json",
"to_json",
[gens.arg_typed("t", t.AnonymousType(basename(src_module_name) <> "." <> type_name))],
[
gens.call("json.object", [
gens.list(
list.map(variant.fields, fn(field) {
case option.to_result(field.label, Nil) {
Ok(label) -> {
// io.debug(#(field))
gens.TupleVal([
gens.StringVal(label),
serializer_of_t(field.item, label),
])
}
_ -> {
io.println_error(
"Variant "
<> variant.name
<> " must have labels for all fields",
)
panic as "missing label"
}
}
}),
),
]),
],
)
}
fn gen_to_string(req) {
let Request(src_module_name: src_module_name, type_name: type_name, ..) = req
gens.Function(
"to_string",
[gens.arg_typed("t", t.AnonymousType(basename(src_module_name) <> "." <> type_name))],
[gens.call("json.to_string", [gens.call("to_json", [gens.variable("t")])])],
)
}
pub fn from(req: request.Request) {
[gen_to_json(req), gen_to_string(req)]
|> list.map(gens.generate)
|> string.join(with: "\n")
}