Current section
Files
Jump to
Current section
Files
src/libero@json@codegen.erl
-module(libero@json@codegen).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/libero/json/codegen.gleam").
-export([generate/1, generate_transport_codecs_with_push_and_ssr/3]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Typed JSON encoder and decoder codegen.\n"
"\n"
" Generates Gleam source code for per-type `json.Json` builders and\n"
" typed decoders. Each discovered type gets a pair of functions:\n"
" `json_encode_<qualified_atom_name>` — builds a `json.Json` value\n"
" `json_decode_<qualified_atom_name>` — validates and decodes from `Dynamic`\n"
"\n"
" The generated wire format follows the JSON-transport-v1 contract shape:\n"
" { \"type\": \"<module>.<type_name>\",\n"
" \"variant\": \"<variant_name>\",\n"
" \"fields\": <object or array> }\n"
"\n"
" Labelled variants encode fields as a JSON object under \"fields\";\n"
" unlabelled variants encode them as a JSON array. Mixed labelled /\n"
" unlabelled constructors are rejected at codegen time.\n"
).
-file("src/libero/json/codegen.gleam", 1186).
?DOC(" Emit the builtin Result decoder function.\n").
-spec emit_result_decoder() -> binary().
emit_result_decoder() ->
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"pub fn json_decode_gleam_result__result(value: Dynamic, decode_ok: fn(Dynamic) -> Result(a, List(JsonError)), decode_err: fn(Dynamic) -> Result(b, List(JsonError))) -> Result(Result(a, b), List(JsonError)) {\n"/utf8,
" use _ <- result.try(\n"/utf8>>/binary,
" case json_runtime.field_string(value, \"type\", \"type\") {\n"/utf8>>/binary,
" Ok(\"gleam/result.Result\") -> Ok(Nil)\n"/utf8>>/binary,
" Ok(s) -> Error([JsonError(\"type\", \"expected gleam/result.Result, got \" <> s)])\n"/utf8>>/binary,
" Error(_) -> Error([JsonError(\"type\", \"missing or not a string\")])\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" )\n"/utf8>>/binary,
" case json_runtime.field_string(value, \"variant\", \"variant\") {\n"/utf8>>/binary,
" Ok(\"Ok\") -> {\n"/utf8>>/binary,
" use fields <- result.try(json_runtime.field(value, \"fields\", \"fields\"))\n"/utf8>>/binary,
" use size <- result.try(json_runtime.array_length(fields, \"fields\"))\n"/utf8>>/binary,
" use _ <- result.try(case size { 1 -> Ok(Nil) n -> Error([JsonError(\"fields\", \"expected 1 elements, got \" <> int.to_string(n))]) })\n"/utf8>>/binary,
" use inner_raw <- result.try(json_runtime.array_at(fields, 0, \"fields[0]\"))\n"/utf8>>/binary,
" use val <- result.try(decode_ok(inner_raw))\n"/utf8>>/binary,
" Ok(Ok(val))\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" Ok(\"Error\") -> {\n"/utf8>>/binary,
" use fields <- result.try(json_runtime.field(value, \"fields\", \"fields\"))\n"/utf8>>/binary,
" use size <- result.try(json_runtime.array_length(fields, \"fields\"))\n"/utf8>>/binary,
" use _ <- result.try(case size { 1 -> Ok(Nil) n -> Error([JsonError(\"fields\", \"expected 1 elements, got \" <> int.to_string(n))]) })\n"/utf8>>/binary,
" use inner_raw <- result.try(json_runtime.array_at(fields, 0, \"fields[0]\"))\n"/utf8>>/binary,
" use val <- result.try(decode_err(inner_raw))\n"/utf8>>/binary,
" Ok(Error(val))\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" Ok(other) -> Error([JsonError(\"variant\", \"unknown Result variant: \" <> other)])\n"/utf8>>/binary,
" Error(_) -> Error([JsonError(\"variant\", \"missing or not a string\")])\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
"}"/utf8>>.
-file("src/libero/json/codegen.gleam", 1133).
?DOC(" Emit the builtin Option decoder function.\n").
-spec emit_option_decoder() -> binary().
emit_option_decoder() ->
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"pub fn json_decode_gleam_option__option(value: Dynamic, decode_inner: fn(Dynamic) -> Result(a, List(JsonError))) -> Result(Option(a), List(JsonError)) {\n"/utf8,
" use _ <- result.try(\n"/utf8>>/binary,
" case json_runtime.field_string(value, \"type\", \"type\") {\n"/utf8>>/binary,
" Ok(\"gleam/option.Option\") -> Ok(Nil)\n"/utf8>>/binary,
" Ok(s) -> Error([JsonError(\"type\", \"expected gleam/option.Option, got \" <> s)])\n"/utf8>>/binary,
" Error(_) -> Error([JsonError(\"type\", \"missing or not a string\")])\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" )\n"/utf8>>/binary,
" case json_runtime.field_string(value, \"variant\", \"variant\") {\n"/utf8>>/binary,
" Ok(\"None\") -> {\n"/utf8>>/binary,
" use fields <- result.try(json_runtime.field(value, \"fields\", \"fields\"))\n"/utf8>>/binary,
" use size <- result.try(json_runtime.object_size(fields, \"fields\"))\n"/utf8>>/binary,
" case size {\n"/utf8>>/binary,
" 0 -> Ok(None)\n"/utf8>>/binary,
" n -> Error([JsonError(\"fields\", \"expected exactly 0 object fields, got \" <> int.to_string(n))])\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" Ok(\"Some\") -> {\n"/utf8>>/binary,
" use fields <- result.try(json_runtime.field(value, \"fields\", \"fields\"))\n"/utf8>>/binary,
" use size <- result.try(json_runtime.array_length(fields, \"fields\"))\n"/utf8>>/binary,
" use _ <- result.try(case size { 1 -> Ok(Nil) n -> Error([JsonError(\"fields\", \"expected 1 elements, got \" <> int.to_string(n))]) })\n"/utf8>>/binary,
" use inner_raw <- result.try(json_runtime.array_at(fields, 0, \"fields[0]\"))\n"/utf8>>/binary,
" use val <- result.try(decode_inner(inner_raw))\n"/utf8>>/binary,
" Ok(Some(val))\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" Ok(other) -> Error([JsonError(\"variant\", \"unknown Option variant: \" <> other)])\n"/utf8>>/binary,
" Error(_) -> Error([JsonError(\"variant\", \"missing or not a string\")])\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
"}"/utf8>>.
-file("src/libero/json/codegen.gleam", 1167).
?DOC(" Emit the builtin Result encoder function.\n").
-spec emit_result_encoder() -> binary().
emit_result_encoder() ->
<<<<<<<<<<<<<<<<<<<<<<<<<<"pub fn json_encode_gleam_result__result(value, encode_ok: fn(a) -> json.Json, encode_err: fn(b) -> json.Json) -> json.Json {\n"/utf8,
" case value {\n"/utf8>>/binary,
" Ok(inner) -> json.object([\n"/utf8>>/binary,
" #(\"type\", json.string(\"gleam/result.Result\")),\n"/utf8>>/binary,
" #(\"variant\", json.string(\"Ok\")),\n"/utf8>>/binary,
" #(\"fields\", json.array([encode_ok(inner)], of: fn(x) { x })),\n"/utf8>>/binary,
" ])\n"/utf8>>/binary,
" Error(inner) -> json.object([\n"/utf8>>/binary,
" #(\"type\", json.string(\"gleam/result.Result\")),\n"/utf8>>/binary,
" #(\"variant\", json.string(\"Error\")),\n"/utf8>>/binary,
" #(\"fields\", json.array([encode_err(inner)], of: fn(x) { x })),\n"/utf8>>/binary,
" ])\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
"}"/utf8>>.
-file("src/libero/json/codegen.gleam", 1114).
?DOC(" Emit the builtin Option encoder function.\n").
-spec emit_option_encoder() -> binary().
emit_option_encoder() ->
<<<<<<<<<<<<<<<<<<<<<<<<<<"pub fn json_encode_gleam_option__option(value, encode_inner: fn(a) -> json.Json) -> json.Json {\n"/utf8,
" case value {\n"/utf8>>/binary,
" Some(inner) -> json.object([\n"/utf8>>/binary,
" #(\"type\", json.string(\"gleam/option.Option\")),\n"/utf8>>/binary,
" #(\"variant\", json.string(\"Some\")),\n"/utf8>>/binary,
" #(\"fields\", json.array([encode_inner(inner)], of: fn(x) { x })),\n"/utf8>>/binary,
" ])\n"/utf8>>/binary,
" None -> json.object([\n"/utf8>>/binary,
" #(\"type\", json.string(\"gleam/option.Option\")),\n"/utf8>>/binary,
" #(\"variant\", json.string(\"None\")),\n"/utf8>>/binary,
" #(\"fields\", json.object([])),\n"/utf8>>/binary,
" ])\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
"}"/utf8>>.
-file("src/libero/json/codegen.gleam", 940).
?DOC(" Build positional constructor field names e.g. `f0, f1`\n").
-spec emit_variant_constructor_positional(libero@walker:discovered_variant()) -> binary().
emit_variant_constructor_positional(V) ->
Names = gleam@list:index_map(
erlang:element(7, V),
fun(_, I) -> <<"f"/utf8, (erlang:integer_to_binary(I))/binary>> end
),
gleam@string:join(Names, <<", "/utf8>>).
-file("src/libero/json/codegen.gleam", 928).
?DOC(" Build labelled constructor field names e.g. `title:, body:`\n").
-spec emit_variant_constructor_labels(libero@walker:discovered_variant()) -> binary().
emit_variant_constructor_labels(V) ->
Names = gleam@list:filter_map(erlang:element(6, V), fun(L) -> case L of
{some, N} ->
{ok, <<N/binary, ":"/utf8>>};
none ->
{error, nil}
end end),
gleam@string:join(Names, <<", "/utf8>>).
-file("src/libero/json/codegen.gleam", 1330).
?DOC(" Generate inline decode for Tuple types.\n").
-spec emit_tuple_decode(
list(libero@field_type:field_type()),
binary(),
binary(),
binary()
) -> binary().
emit_tuple_decode(Elements, Raw_var, Path, Pad) ->
Element_count = erlang:length(Elements),
N_str = erlang:integer_to_binary(Element_count),
Fields_pad = <<Pad/binary, " "/utf8>>,
Field_decodes = gleam@list:index_map(
Elements,
fun(Ft, I) ->
Idx_str = erlang:integer_to_binary(I),
Var_name = <<"t"/utf8, Idx_str/binary>>,
Result_var = <<Var_name/binary, "_result"/utf8>>,
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Fields_pad/binary,
"let "/utf8>>/binary,
Result_var/binary>>/binary,
" = case json_runtime.array_at("/utf8>>/binary,
Raw_var/binary>>/binary,
", "/utf8>>/binary,
Idx_str/binary>>/binary,
", \""/utf8>>/binary,
Path/binary>>/binary,
"["/utf8>>/binary,
Idx_str/binary>>/binary,
"]\") {\n"/utf8>>/binary,
Fields_pad/binary>>/binary,
" Ok(raw) -> "/utf8>>/binary,
(emit_raw_value_decode(
Ft,
<<"raw"/utf8>>,
<<<<<<Path/binary,
"["/utf8>>/binary,
Idx_str/binary>>/binary,
"]"/utf8>>,
<<Fields_pad/binary,
" "/utf8>>
))/binary>>/binary,
"\n"/utf8>>/binary,
Fields_pad/binary>>/binary,
" Error(errors) -> Error(errors)\n"/utf8>>/binary,
Fields_pad/binary>>/binary,
"}\n"/utf8>>/binary,
Fields_pad/binary>>/binary,
"use "/utf8>>/binary,
Var_name/binary>>/binary,
" <- result.try("/utf8>>/binary,
Result_var/binary>>/binary,
")\n"/utf8>>
end
),
Tuple_expr = <<<<"#("/utf8,
(gleam@string:join(
gleam@list:index_map(
Elements,
fun(_, I@1) ->
<<"t"/utf8, (erlang:integer_to_binary(I@1))/binary>>
end
),
<<", "/utf8>>
))/binary>>/binary,
")"/utf8>>,
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Pad/binary,
"case json_runtime.array_length("/utf8>>/binary,
Raw_var/binary>>/binary,
", \""/utf8>>/binary,
Path/binary>>/binary,
"\") {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(size) -> case size {\n"/utf8>>/binary,
Pad/binary>>/binary,
" "/utf8>>/binary,
N_str/binary>>/binary,
" -> {\n"/utf8>>/binary,
(gleam@string:join(
Field_decodes,
<<""/utf8>>
))/binary>>/binary,
Fields_pad/binary>>/binary,
"Ok("/utf8>>/binary,
Tuple_expr/binary>>/binary,
")\n"/utf8>>/binary,
Pad/binary>>/binary,
" }\n"/utf8>>/binary,
Pad/binary>>/binary,
" n -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
"\", \"expected "/utf8>>/binary,
N_str/binary>>/binary,
" elements, got \" <> int.to_string(n))])\n"/utf8>>/binary,
Pad/binary>>/binary,
" }\n"/utf8>>/binary,
Pad/binary>>/binary,
" Error(errors) -> Error(errors)\n"/utf8>>/binary,
Pad/binary>>/binary,
"}"/utf8>>.
-file("src/libero/json/codegen.gleam", 1220).
?DOC(" Generate inline decode for Dict types.\n").
-spec emit_dict_decode(
libero@field_type:field_type(),
libero@field_type:field_type(),
binary(),
binary(),
binary()
) -> binary().
emit_dict_decode(Key, Value, Raw_var, Path, Pad) ->
case Key of
string_field ->
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Pad/binary,
"case json_runtime.object_entries("/utf8>>/binary,
Raw_var/binary>>/binary,
", \""/utf8>>/binary,
Path/binary>>/binary,
"\") {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(entries) -> {\n"/utf8>>/binary,
Pad/binary>>/binary,
" use decoded <- result.try(\n"/utf8>>/binary,
Pad/binary>>/binary,
" list.try_map(entries, fn(entry) {\n"/utf8>>/binary,
Pad/binary>>/binary,
" let k = entry.0\n"/utf8>>/binary,
Pad/binary>>/binary,
" let v_raw = entry.1\n"/utf8>>/binary,
Pad/binary>>/binary,
" use v <- result.try(\n"/utf8>>/binary,
(emit_raw_value_decode(
Value,
<<"v_raw"/utf8>>,
<<Path/binary,
".value"/utf8>>,
<<Pad/binary,
" "/utf8>>
))/binary>>/binary,
"\n"/utf8>>/binary,
Pad/binary>>/binary,
" )\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(#(k, v))\n"/utf8>>/binary,
Pad/binary>>/binary,
" })\n"/utf8>>/binary,
Pad/binary>>/binary,
" )\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(dict.from_list(decoded))\n"/utf8>>/binary,
Pad/binary>>/binary,
" }\n"/utf8>>/binary,
Pad/binary>>/binary,
" Error(errors) -> Error(errors)\n"/utf8>>/binary,
Pad/binary>>/binary,
"}"/utf8>>;
_ ->
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Pad/binary,
"case json_runtime.pair_entries("/utf8>>/binary,
Raw_var/binary>>/binary,
", \""/utf8>>/binary,
Path/binary>>/binary,
"\") {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(pairs_raw) -> {\n"/utf8>>/binary,
Pad/binary>>/binary,
" use pairs <- result.try(\n"/utf8>>/binary,
Pad/binary>>/binary,
" list.try_map(pairs_raw, fn(pair_raw) {\n"/utf8>>/binary,
Pad/binary>>/binary,
" let k_raw = pair_raw.0\n"/utf8>>/binary,
Pad/binary>>/binary,
" let v_raw = pair_raw.1\n"/utf8>>/binary,
Pad/binary>>/binary,
" use k <- result.try(\n"/utf8>>/binary,
(emit_raw_value_decode(
Key,
<<"k_raw"/utf8>>,
<<Path/binary,
".key"/utf8>>,
<<Pad/binary,
" "/utf8>>
))/binary>>/binary,
"\n"/utf8>>/binary,
Pad/binary>>/binary,
" )\n"/utf8>>/binary,
Pad/binary>>/binary,
" use v <- result.try(\n"/utf8>>/binary,
(emit_raw_value_decode(
Value,
<<"v_raw"/utf8>>,
<<Path/binary,
".value"/utf8>>,
<<Pad/binary,
" "/utf8>>
))/binary>>/binary,
"\n"/utf8>>/binary,
Pad/binary>>/binary,
" )\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(#(k, v))\n"/utf8>>/binary,
Pad/binary>>/binary,
" })\n"/utf8>>/binary,
Pad/binary>>/binary,
" )\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(dict.from_list(pairs))\n"/utf8>>/binary,
Pad/binary>>/binary,
" }\n"/utf8>>/binary,
Pad/binary>>/binary,
" Error(errors) -> Error(errors)\n"/utf8>>/binary,
Pad/binary>>/binary,
"}"/utf8>>
end.
-file("src/libero/json/codegen.gleam", 947).
?DOC(
" Emit a decode expression for a raw Dynamic value according to FieldType.\n"
" Returns a single expression evaluating to `Result(value, List(JsonError))`.\n"
).
-spec emit_raw_value_decode(
libero@field_type:field_type(),
binary(),
binary(),
binary()
) -> binary().
emit_raw_value_decode(Ft, Raw_var, Path, Pad) ->
case Ft of
string_field ->
<<<<<<<<<<Pad/binary, "json_runtime.string("/utf8>>/binary,
Raw_var/binary>>/binary,
", \""/utf8>>/binary,
Path/binary>>/binary,
"\")"/utf8>>;
int_field ->
<<<<<<<<<<Pad/binary, "json_runtime.int("/utf8>>/binary,
Raw_var/binary>>/binary,
", \""/utf8>>/binary,
Path/binary>>/binary,
"\")"/utf8>>;
float_field ->
<<<<<<<<<<Pad/binary, "json_runtime.float("/utf8>>/binary,
Raw_var/binary>>/binary,
", \""/utf8>>/binary,
Path/binary>>/binary,
"\")"/utf8>>;
bool_field ->
<<<<<<<<<<Pad/binary, "json_runtime.bool("/utf8>>/binary,
Raw_var/binary>>/binary,
", \""/utf8>>/binary,
Path/binary>>/binary,
"\")"/utf8>>;
nil_field ->
<<<<<<<<<<Pad/binary, "json_runtime.nil("/utf8>>/binary,
Raw_var/binary>>/binary,
", \""/utf8>>/binary,
Path/binary>>/binary,
"\")"/utf8>>;
bit_array_field ->
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Pad/binary,
"case json_runtime.field_string("/utf8>>/binary,
Raw_var/binary>>/binary,
", \"encoding\", \""/utf8>>/binary,
Path/binary>>/binary,
".encoding\") {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(\"base64url\") -> case json_runtime.field_string("/utf8>>/binary,
Raw_var/binary>>/binary,
", \"data\", \""/utf8>>/binary,
Path/binary>>/binary,
".data\") {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(s) -> case bit_array.base64_url_decode(s) {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(bits) -> Ok(bits)\n"/utf8>>/binary,
Pad/binary>>/binary,
" Error(_) -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
".data\", \"expected valid base64url BitArray data\")])\n"/utf8>>/binary,
Pad/binary>>/binary,
" }\n"/utf8>>/binary,
Pad/binary>>/binary,
" Error(_) -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
".data\", \"missing or not a string\")])\n"/utf8>>/binary,
Pad/binary>>/binary,
" }\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(other) -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
".encoding\", \"expected base64url BitArray encoding, got \" <> other)])\n"/utf8>>/binary,
Pad/binary>>/binary,
" Error(_) -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
".encoding\", \"missing or not a string\")])\n"/utf8>>/binary,
Pad/binary>>/binary,
"}"/utf8>>;
{user_type, Module_path, Type_name, _} ->
Qual = libero@walker:qualified_atom_name(Module_path, Type_name),
<<<<<<<<<<Pad/binary, "json_decode_"/utf8>>/binary, Qual/binary>>/binary,
"("/utf8>>/binary,
Raw_var/binary>>/binary,
")"/utf8>>;
{list_of, Element} ->
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Pad/binary,
"case json_runtime.list("/utf8>>/binary,
Raw_var/binary>>/binary,
", \""/utf8>>/binary,
Path/binary>>/binary,
"\") {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(items) -> list.try_map(items, fn(item_raw) {\n"/utf8>>/binary,
(emit_raw_value_decode(
Element,
<<"item_raw"/utf8>>,
<<Path/binary, "[]"/utf8>>,
<<Pad/binary, " "/utf8>>
))/binary>>/binary,
"\n"/utf8>>/binary,
Pad/binary>>/binary,
" })\n"/utf8>>/binary,
Pad/binary>>/binary,
" Error(errors) -> Error(errors)\n"/utf8>>/binary,
Pad/binary>>/binary,
"}"/utf8>>;
{option_of, Inner} ->
Qual@1 = libero@walker:qualified_atom_name(
<<"gleam/option"/utf8>>,
<<"Option"/utf8>>
),
<<<<<<<<<<<<<<<<<<Pad/binary, "json_decode_"/utf8>>/binary,
Qual@1/binary>>/binary,
"("/utf8>>/binary,
Raw_var/binary>>/binary,
", fn(inner_raw) {\n"/utf8>>/binary,
(emit_raw_value_decode(
Inner,
<<"inner_raw"/utf8>>,
<<Path/binary, ".value"/utf8>>,
<<Pad/binary, " "/utf8>>
))/binary>>/binary,
"\n"/utf8>>/binary,
Pad/binary>>/binary,
"})"/utf8>>;
{result_of, Ok, Err} ->
Qual@2 = libero@walker:qualified_atom_name(
<<"gleam/result"/utf8>>,
<<"Result"/utf8>>
),
<<<<<<<<<<<<<<<<<<<<<<<<<<Pad/binary, "json_decode_"/utf8>>/binary,
Qual@2/binary>>/binary,
"("/utf8>>/binary,
Raw_var/binary>>/binary,
", fn(inner_raw) {\n"/utf8>>/binary,
(emit_raw_value_decode(
Ok,
<<"inner_raw"/utf8>>,
<<Path/binary, ".ok"/utf8>>,
<<Pad/binary, " "/utf8>>
))/binary>>/binary,
"\n"/utf8>>/binary,
Pad/binary>>/binary,
"}, fn(inner_raw) {\n"/utf8>>/binary,
(emit_raw_value_decode(
Err,
<<"inner_raw"/utf8>>,
<<Path/binary, ".error"/utf8>>,
<<Pad/binary, " "/utf8>>
))/binary>>/binary,
"\n"/utf8>>/binary,
Pad/binary>>/binary,
"})"/utf8>>;
{dict_of, Key, Value} ->
emit_dict_decode(Key, Value, Raw_var, Path, Pad);
{tuple_of, Elements} ->
emit_tuple_decode(Elements, Raw_var, Path, Pad);
{type_var, Name} ->
<<<<<<<<<<Pad/binary, "Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
"\", \"cannot decode type variable "/utf8>>/binary,
Name/binary>>/binary,
"\")])"/utf8>>
end.
-file("src/libero/json/codegen.gleam", 860).
?DOC(" Generate field decode bindings for unlabelled fields (extracted by index).\n").
-spec emit_unlabelled_field_decodes(
libero@walker:discovered_variant(),
binary()
) -> binary().
emit_unlabelled_field_decodes(V, Pad) ->
Field_count = erlang:length(erlang:element(7, V)),
N_str = erlang:integer_to_binary(Field_count),
Length_check = <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Pad/binary,
"use arr <- result.try(\n"/utf8>>/binary,
Pad/binary>>/binary,
" case json_runtime.array_length(fields, \"fields\") {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(size) -> case size {\n"/utf8>>/binary,
Pad/binary>>/binary,
" "/utf8>>/binary,
N_str/binary>>/binary,
" -> Ok(fields)\n"/utf8>>/binary,
Pad/binary>>/binary,
" n -> Error([JsonError(\"fields\", \"expected "/utf8>>/binary,
N_str/binary>>/binary,
" elements, got \" <> int.to_string(n))])\n"/utf8>>/binary,
Pad/binary>>/binary,
" }\n"/utf8>>/binary,
Pad/binary>>/binary,
" Error(errors) -> Error(errors)\n"/utf8>>/binary,
Pad/binary>>/binary,
" }\n"/utf8>>/binary,
Pad/binary>>/binary,
")\n"/utf8>>,
Entries = gleam@list:index_map(
erlang:element(7, V),
fun(Ft, I) ->
Idx_str = erlang:integer_to_binary(I),
Path = <<<<"fields["/utf8, Idx_str/binary>>/binary, "]"/utf8>>,
Var_name = <<"f"/utf8, Idx_str/binary>>,
Result_var = <<Var_name/binary, "_result"/utf8>>,
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Pad/binary, "let "/utf8>>/binary,
Result_var/binary>>/binary,
" = case json_runtime.array_at(arr, "/utf8>>/binary,
Idx_str/binary>>/binary,
", \""/utf8>>/binary,
Path/binary>>/binary,
"\") {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(raw) -> "/utf8>>/binary,
(emit_raw_value_decode(
Ft,
<<"raw"/utf8>>,
Path,
<<Pad/binary,
" "/utf8>>
))/binary>>/binary,
"\n"/utf8>>/binary,
Pad/binary>>/binary,
" Error(errors) -> Error(errors)\n"/utf8>>/binary,
Pad/binary>>/binary,
"}\n"/utf8>>/binary,
Pad/binary>>/binary,
"use "/utf8>>/binary,
Var_name/binary>>/binary,
" <- result.try("/utf8>>/binary,
Result_var/binary>>/binary,
")\n"/utf8>>
end
),
<<Length_check/binary, (gleam@string:join(Entries, <<""/utf8>>))/binary>>.
-file("src/libero/json/codegen.gleam", 815).
?DOC(" Generate field decode bindings for labelled fields (extracted by name).\n").
-spec emit_labelled_field_decodes(libero@walker:discovered_variant(), binary()) -> binary().
emit_labelled_field_decodes(V, Pad) ->
Zipped = gleam@list:zip(erlang:element(7, V), erlang:element(6, V)),
Entries = gleam@list:index_map(
Zipped,
fun(Pair, _) ->
{Ft, Label} = Pair,
case Label of
{some, Name} ->
Path = <<"fields."/utf8, Name/binary>>,
Result_var = <<Name/binary, "_result"/utf8>>,
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Pad/binary,
"let "/utf8>>/binary,
Result_var/binary>>/binary,
" = case json_runtime.field(fields, \""/utf8>>/binary,
Name/binary>>/binary,
"\", \""/utf8>>/binary,
Path/binary>>/binary,
"\") {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(raw) -> "/utf8>>/binary,
(emit_raw_value_decode(
Ft,
<<"raw"/utf8>>,
Path,
<<Pad/binary,
" "/utf8>>
))/binary>>/binary,
"\n"/utf8>>/binary,
Pad/binary>>/binary,
" Error(errors) -> Error(errors)\n"/utf8>>/binary,
Pad/binary>>/binary,
"}\n"/utf8>>/binary,
Pad/binary>>/binary,
"use "/utf8>>/binary,
Name/binary>>/binary,
" <- result.try("/utf8>>/binary,
Result_var/binary>>/binary,
")\n"/utf8>>;
none ->
<<""/utf8>>
end
end
),
gleam@string:join(Entries, <<""/utf8>>).
-file("src/libero/json/codegen.gleam", 780).
-spec emit_fields_object_size_check(binary(), integer(), binary()) -> binary().
emit_fields_object_size_check(Path, Expected_size, Pad) ->
Expected = erlang:integer_to_binary(Expected_size),
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Pad/binary,
"use _ <- result.try(\n"/utf8>>/binary,
Pad/binary>>/binary,
" case json_runtime.object_size(fields, \""/utf8>>/binary,
Path/binary>>/binary,
"\") {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(size) -> case size {\n"/utf8>>/binary,
Pad/binary>>/binary,
" "/utf8>>/binary,
Expected/binary>>/binary,
" -> Ok(Nil)\n"/utf8>>/binary,
Pad/binary>>/binary,
" n -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
"\", \"expected exactly "/utf8>>/binary,
Expected/binary>>/binary,
" object fields, got \" <> int.to_string(n))])\n"/utf8>>/binary,
Pad/binary>>/binary,
" }\n"/utf8>>/binary,
Pad/binary>>/binary,
" Error(errors) -> Error(errors)\n"/utf8>>/binary,
Pad/binary>>/binary,
" }\n"/utf8>>/binary,
Pad/binary>>/binary,
")\n"/utf8>>.
-file("src/libero/json/codegen.gleam", 771).
-spec emit_fields_extract(binary()) -> binary().
emit_fields_extract(Pad) ->
<<<<<<<<<<Pad/binary, "use fields <- result.try(\n"/utf8>>/binary,
Pad/binary>>/binary,
" json_runtime.field(value, \"fields\", \"fields\")\n"/utf8>>/binary,
Pad/binary>>/binary,
")\n"/utf8>>.
-file("src/libero/json/codegen.gleam", 708).
?DOC(
" Emit a single decode clause that matches a variant name string and\n"
" decodes the variant's fields, constructing the Gleam value.\n"
).
-spec emit_decode_clause(libero@walker:discovered_variant(), binary()) -> binary().
emit_decode_clause(V, Alias) ->
Pad = <<" "/utf8>>,
case erlang:element(7, V) of
[] ->
<<<<<<<<<<<<<<<<<<<<<<<<<<Pad/binary, "Ok(\""/utf8>>/binary,
(erlang:element(
3,
V
))/binary>>/binary,
"\") -> {\n"/utf8>>/binary,
(emit_fields_extract(
<<Pad/binary,
" "/utf8>>
))/binary>>/binary,
(emit_fields_object_size_check(
<<"fields"/utf8>>,
0,
<<Pad/binary, " "/utf8>>
))/binary>>/binary,
Pad/binary>>/binary,
" Ok("/utf8>>/binary,
Alias/binary>>/binary,
"."/utf8>>/binary,
(erlang:element(3, V))/binary>>/binary,
")\n"/utf8>>/binary,
Pad/binary>>/binary,
"}\n"/utf8>>;
_ ->
All_labelled = gleam@list:all(
erlang:element(6, V),
fun(L) -> L /= none end
),
Fields_extract = emit_fields_extract(<<Pad/binary, " "/utf8>>),
Field_decodes = case All_labelled of
true ->
<<(emit_fields_object_size_check(
<<"fields"/utf8>>,
erlang:length(erlang:element(7, V)),
<<Pad/binary, " "/utf8>>
))/binary,
(emit_labelled_field_decodes(
V,
<<Pad/binary, " "/utf8>>
))/binary>>;
false ->
emit_unlabelled_field_decodes(V, <<Pad/binary, " "/utf8>>)
end,
Field_names = case All_labelled of
true ->
emit_variant_constructor_labels(V);
false ->
emit_variant_constructor_positional(V)
end,
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Pad/binary, "Ok(\""/utf8>>/binary,
(erlang:element(
3,
V
))/binary>>/binary,
"\") -> {\n"/utf8>>/binary,
Fields_extract/binary>>/binary,
Field_decodes/binary>>/binary,
Pad/binary>>/binary,
" Ok("/utf8>>/binary,
Alias/binary>>/binary,
"."/utf8>>/binary,
(erlang:element(3, V))/binary>>/binary,
"("/utf8>>/binary,
Field_names/binary>>/binary,
"))\n"/utf8>>/binary,
Pad/binary>>/binary,
"}\n"/utf8>>
end.
-file("src/libero/json/codegen.gleam", 667).
?DOC(
" Emit a per-type decoder function that validates the wire format and\n"
" dispatches to per-variant decoding.\n"
).
-spec emit_type_decoder(
libero@walker:discovered_type(),
gleam@dict:dict(binary(), binary())
) -> binary().
emit_type_decoder(Dt, Aliases) ->
Qual = libero@walker:qualified_atom_name(
erlang:element(2, Dt),
erlang:element(3, Dt)
),
Alias = gleam@result:unwrap(
gleam_stdlib:map_get(Aliases, erlang:element(2, Dt)),
libero@field_type:last_segment(erlang:element(2, Dt))
),
Clauses = gleam@list:map(
erlang:element(5, Dt),
fun(V) -> emit_decode_clause(V, Alias) end
),
Type_str = <<<<(erlang:element(2, Dt))/binary, "."/utf8>>/binary,
(erlang:element(3, Dt))/binary>>,
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"pub fn json_decode_"/utf8,
Qual/binary>>/binary,
"(value: dynamic.Dynamic) {\n"/utf8>>/binary,
" use _ <- result.try(\n"/utf8>>/binary,
" case json_runtime.field_string(value, \"type\", \"type\") {\n"/utf8>>/binary,
" Ok(s) if s == \""/utf8>>/binary,
Type_str/binary>>/binary,
"\" -> Ok(s)\n"/utf8>>/binary,
" Ok(s) -> Error([JsonError(\"type\", \"expected "/utf8>>/binary,
Type_str/binary>>/binary,
", got \" <> s)])\n"/utf8>>/binary,
" Error(_) -> Error([JsonError(\"type\", \"missing or not a string\")])\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" )\n"/utf8>>/binary,
" case json_runtime.field_string(value, \"variant\", \"variant\") {\n"/utf8>>/binary,
(gleam@string:join(Clauses, <<"\n"/utf8>>))/binary>>/binary,
" Error(_) -> Error([JsonError(\"variant\", \"missing or not a string\")])\n"/utf8>>/binary,
" Ok(s) -> Error([JsonError(\"variant\", \"unknown: \" <> s)])\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
"}"/utf8>>.
-file("src/libero/json/codegen.gleam", 438).
?DOC(
" Validate that a Float value is finite (not NaN or Infinity).\n"
" NaN/Infinity can arise from FFI on the JavaScript runtime.\n"
" Panics with a descriptive message if the value is not finite.\n"
).
-spec finite_float_check(binary()) -> binary().
finite_float_check(Var) ->
<<<<<<<<<<<<<<<<<<"case "/utf8, Var/binary>>/binary, " -. "/utf8>>/binary,
Var/binary>>/binary,
" == 0.0 {\n"/utf8>>/binary,
" True -> "/utf8>>/binary,
Var/binary>>/binary,
"\n"/utf8>>/binary,
" False -> panic as \"Float must be finite (not NaN or Infinity)\"\n"/utf8>>/binary,
" }"/utf8>>.
-file("src/libero/json/codegen.gleam", 422).
?DOC(
" Validate that an Int value is within JavaScript safe integer range.\n"
" Panics with a descriptive message if the value is outside the range.\n"
).
-spec safe_int_check(binary()) -> binary().
safe_int_check(Var) ->
<<<<<<<<<<<<<<<<<<"case "/utf8, Var/binary>>/binary,
" >= -9007199254740991 && "/utf8>>/binary,
Var/binary>>/binary,
" <= 9007199254740991 {\n"/utf8>>/binary,
" True -> "/utf8>>/binary,
Var/binary>>/binary,
"\n"/utf8>>/binary,
" False -> panic as \"Int outside JavaScript safe integer range\"\n"/utf8>>/binary,
" }"/utf8>>.
-file("src/libero/json/codegen.gleam", 453).
?DOC(
" Wrap a raw Gleam value variable with the appropriate `json.X()` constructor\n"
" so it becomes a `json.Json` expression.\n"
).
-spec json_encode_expr(libero@field_type:field_type(), binary()) -> binary().
json_encode_expr(Ft, Var) ->
case Ft of
string_field ->
<<<<"json.string("/utf8, Var/binary>>/binary, ")"/utf8>>;
int_field ->
<<<<"json.int("/utf8, (safe_int_check(Var))/binary>>/binary,
")"/utf8>>;
float_field ->
<<<<"json.float("/utf8, (finite_float_check(Var))/binary>>/binary,
")"/utf8>>;
bool_field ->
<<<<"json.bool("/utf8, Var/binary>>/binary, ")"/utf8>>;
nil_field ->
<<"json.null()"/utf8>>;
bit_array_field ->
<<<<"json.object([#(\"encoding\", json.string(\"base64url\")), #(\"data\", json.string(bit_array.base64_url_encode("/utf8,
Var/binary>>/binary,
", True)))])"/utf8>>;
{user_type, Module_path, Type_name, _} ->
Qual = libero@walker:qualified_atom_name(Module_path, Type_name),
<<<<<<<<"json_encode_"/utf8, Qual/binary>>/binary, "("/utf8>>/binary,
Var/binary>>/binary,
")"/utf8>>;
{list_of, Element} ->
<<<<<<<<"json.array("/utf8, Var/binary>>/binary,
", of: fn(x) { "/utf8>>/binary,
(json_encode_expr(Element, <<"x"/utf8>>))/binary>>/binary,
" })"/utf8>>;
{option_of, Inner} ->
Qual@1 = libero@walker:qualified_atom_name(
<<"gleam/option"/utf8>>,
<<"Option"/utf8>>
),
<<<<<<<<<<<<"json_encode_"/utf8, Qual@1/binary>>/binary, "("/utf8>>/binary,
Var/binary>>/binary,
", fn(x) { "/utf8>>/binary,
(json_encode_expr(Inner, <<"x"/utf8>>))/binary>>/binary,
" })"/utf8>>;
{result_of, Ok, Err} ->
Qual@2 = libero@walker:qualified_atom_name(
<<"gleam/result"/utf8>>,
<<"Result"/utf8>>
),
<<<<<<<<<<<<<<<<"json_encode_"/utf8, Qual@2/binary>>/binary,
"("/utf8>>/binary,
Var/binary>>/binary,
", fn(x) { "/utf8>>/binary,
(json_encode_expr(Ok, <<"x"/utf8>>))/binary>>/binary,
" }, fn(x) { "/utf8>>/binary,
(json_encode_expr(Err, <<"x"/utf8>>))/binary>>/binary,
" })"/utf8>>;
{dict_of, Key, Value} ->
case Key of
string_field ->
<<<<<<<<"json.object(dict.to_list("/utf8, Var/binary>>/binary,
") |> list.map(fn(kv) { let #(k, v) = kv #(k, "/utf8>>/binary,
(json_encode_expr(Value, <<"v"/utf8>>))/binary>>/binary,
") }))"/utf8>>;
_ ->
<<<<<<<<<<<<"json.array(dict.to_list("/utf8, Var/binary>>/binary,
"), of: fn(kv) { let #(k, v) = kv json.array(["/utf8>>/binary,
(json_encode_expr(Key, <<"k"/utf8>>))/binary>>/binary,
", "/utf8>>/binary,
(json_encode_expr(Value, <<"v"/utf8>>))/binary>>/binary,
"], of: fn(x) { x }) })"/utf8>>
end;
{tuple_of, Elements} ->
Encoded = gleam@list:index_map(
Elements,
fun(E, I) ->
json_encode_expr(
E,
<<<<Var/binary, "."/utf8>>/binary,
(erlang:integer_to_binary(I))/binary>>
)
end
),
<<<<"json.array(["/utf8,
(gleam@string:join(Encoded, <<", "/utf8>>))/binary>>/binary,
"], of: fn(x) { x })"/utf8>>;
{type_var, Name} ->
<<<<"panic as \"cannot encode type variable "/utf8, Name/binary>>/binary,
"\""/utf8>>
end.
-file("src/libero/json/codegen.gleam", 640).
-spec emit_fields_object(libero@walker:discovered_variant(), list(binary())) -> binary().
emit_fields_object(V, Field_vars) ->
Entries = gleam@list:index_map(
gleam@list:zip(
erlang:element(7, V),
gleam@list:zip(erlang:element(6, V), Field_vars)
),
fun(Pair, I) ->
{Ft, Label_and_var} = Pair,
{Label, Fvar} = Label_and_var,
case Label of
{some, Name} ->
<<<<<<<<"#(\""/utf8, Name/binary>>/binary, "\", "/utf8>>/binary,
(json_encode_expr(Ft, Fvar))/binary>>/binary,
")"/utf8>>;
none ->
<<<<<<<<"#(\"_"/utf8, (erlang:integer_to_binary(I))/binary>>/binary,
"\", "/utf8>>/binary,
(json_encode_expr(Ft, Fvar))/binary>>/binary,
")"/utf8>>
end
end
),
<<<<"json.object(["/utf8,
(gleam@string:join(Entries, <<", "/utf8>>))/binary>>/binary,
"])"/utf8>>.
-file("src/libero/json/codegen.gleam", 627).
-spec emit_unlabelled_fields_array(
libero@walker:discovered_variant(),
list(binary())
) -> binary().
emit_unlabelled_fields_array(V, Field_vars) ->
Encoded = begin
_pipe = gleam@list:zip(erlang:element(7, V), Field_vars),
gleam@list:map(
_pipe,
fun(Pair) ->
{Ft, Fvar} = Pair,
json_encode_expr(Ft, Fvar)
end
)
end,
<<<<"json.array(["/utf8,
(gleam@string:join(Encoded, <<", "/utf8>>))/binary>>/binary,
"], of: fn(x) { x })"/utf8>>.
-file("src/libero/json/codegen.gleam", 614).
?DOC(
" Build the \"fields\" value for a variant.\n"
" - Zero-field variants: `json.object([])`\n"
" - Unlabelled fields: `json.array([f0, f1, ...], of: fn(x) { x })`\n"
" - Labelled fields: `json.object([#(\"label\", f0), ...])`\n"
).
-spec emit_fields_value(libero@walker:discovered_variant(), list(binary())) -> binary().
emit_fields_value(V, Field_vars) ->
case erlang:element(6, V) of
[] ->
<<"json.object([])"/utf8>>;
_ ->
All_unlabelled = gleam@list:all(
erlang:element(6, V),
fun(L) -> L =:= none end
),
case All_unlabelled of
true ->
emit_unlabelled_fields_array(V, Field_vars);
false ->
emit_fields_object(V, Field_vars)
end
end.
-file("src/libero/json/codegen.gleam", 568).
?DOC(" Emit a single encode pattern-match arm for one variant.\n").
-spec emit_encode_clause(libero@walker:discovered_variant(), binary(), binary()) -> binary().
emit_encode_clause(Variant, Alias, Type_name) ->
Field_vars = gleam@list:index_map(
erlang:element(7, Variant),
fun(Field, I) -> case Field of
nil_field ->
<<"_f"/utf8, (erlang:integer_to_binary(I))/binary>>;
_ ->
<<"f"/utf8, (erlang:integer_to_binary(I))/binary>>
end end
),
Fields_expr = emit_fields_value(Variant, Field_vars),
Type_str = <<<<(erlang:element(2, Variant))/binary, "."/utf8>>/binary,
Type_name/binary>>,
Pattern = case Field_vars of
[] ->
<<<<Alias/binary, "."/utf8>>/binary,
(erlang:element(3, Variant))/binary>>;
_ ->
<<<<<<<<<<Alias/binary, "."/utf8>>/binary,
(erlang:element(3, Variant))/binary>>/binary,
"("/utf8>>/binary,
(gleam@string:join(Field_vars, <<", "/utf8>>))/binary>>/binary,
")"/utf8>>
end,
<<<<<<<<<<<<<<<<<<<<<<<<<<" "/utf8, Pattern/binary>>/binary,
" ->\n"/utf8>>/binary,
" json.object([\n"/utf8>>/binary,
" #(\"type\", json.string(\""/utf8>>/binary,
Type_str/binary>>/binary,
"\")),\n"/utf8>>/binary,
" #(\"variant\", json.string(\""/utf8>>/binary,
(erlang:element(3, Variant))/binary>>/binary,
"\")),\n"/utf8>>/binary,
" #(\"fields\", "/utf8>>/binary,
Fields_expr/binary>>/binary,
"),\n"/utf8>>/binary,
" ])"/utf8>>.
-file("src/libero/json/codegen.gleam", 540).
?DOC(
" Emit a per-type encoder function that pattern-matches on the type's\n"
" variants and builds a `json.Json` value with the standard wire shape:\n"
" { \"type\": \"<module>.<type_name>\",\n"
" \"variant\": \"<variant_name>\",\n"
" \"fields\": <per-variant encoding> }\n"
).
-spec emit_type_encoder(
libero@walker:discovered_type(),
gleam@dict:dict(binary(), binary())
) -> binary().
emit_type_encoder(Dt, Aliases) ->
Qual = libero@walker:qualified_atom_name(
erlang:element(2, Dt),
erlang:element(3, Dt)
),
Alias = gleam@result:unwrap(
gleam_stdlib:map_get(Aliases, erlang:element(2, Dt)),
libero@field_type:last_segment(erlang:element(2, Dt))
),
Clauses = gleam@list:map(
erlang:element(5, Dt),
fun(V) -> emit_encode_clause(V, Alias, erlang:element(3, Dt)) end
),
<<<<<<<<<<"pub fn json_encode_"/utf8, Qual/binary>>/binary,
"(value) -> json.Json {\n"/utf8>>/binary,
" case value {\n"/utf8>>/binary,
(gleam@string:join(Clauses, <<"\n"/utf8>>))/binary>>/binary,
"\n }\n}"/utf8>>.
-file("src/libero/json/codegen.gleam", 1451).
-spec uses_list(libero@field_type:field_type()) -> boolean().
uses_list(Ft) ->
case Ft of
{list_of, _} ->
true;
_ ->
false
end.
-file("src/libero/json/codegen.gleam", 1444).
-spec uses_dict(libero@field_type:field_type()) -> boolean().
uses_dict(Ft) ->
case Ft of
{dict_of, _, _} ->
true;
_ ->
false
end.
-file("src/libero/json/codegen.gleam", 1427).
-spec flatten_field_type(libero@field_type:field_type()) -> list(libero@field_type:field_type()).
flatten_field_type(Ft) ->
case Ft of
{list_of, Inner} ->
[Ft | flatten_field_type(Inner)];
{option_of, Inner@1} ->
[Ft | flatten_field_type(Inner@1)];
{result_of, Ok, Err} ->
[Ft | lists:append(flatten_field_type(Ok), flatten_field_type(Err))];
{dict_of, Key, Value} ->
[Ft |
lists:append(flatten_field_type(Key), flatten_field_type(Value))];
{tuple_of, Elements} ->
[Ft | gleam@list:flat_map(Elements, fun flatten_field_type/1)];
_ ->
[Ft]
end.
-file("src/libero/json/codegen.gleam", 1419).
-spec collect_all_fields(list(libero@walker:discovered_type())) -> list(libero@field_type:field_type()).
collect_all_fields(Discovered) ->
gleam@list:flat_map(
Discovered,
fun(Dt) ->
gleam@list:flat_map(
erlang:element(5, Dt),
fun(V) ->
gleam@list:flat_map(
erlang:element(7, V),
fun(Ft) -> flatten_field_type(Ft) end
)
end
)
end
).
-file("src/libero/json/codegen.gleam", 352).
-spec check_supported_field_type(libero@field_type:field_type(), binary()) -> list(libero@json@error:json_error()).
check_supported_field_type(Ft, Path) ->
case Ft of
int_field ->
[];
float_field ->
[];
string_field ->
[];
bool_field ->
[];
bit_array_field ->
[];
nil_field ->
[];
{type_var, Name} ->
[{json_error,
Path,
<<<<"type variable "/utf8, Name/binary>>/binary,
" cannot cross the JSON wire"/utf8>>}];
{user_type, _, _, Args} ->
_pipe = Args,
_pipe@1 = gleam@list:index_map(
_pipe,
fun(Arg, Index) -> {Arg, Index} end
),
gleam@list:flat_map(
_pipe@1,
fun(Pair) ->
{Arg@1, Index@1} = Pair,
check_supported_field_type(
Arg@1,
<<<<<<Path/binary, ".arg["/utf8>>/binary,
(erlang:integer_to_binary(Index@1))/binary>>/binary,
"]"/utf8>>
)
end
);
{list_of, Element} ->
check_supported_field_type(
Element,
<<Path/binary, ".element"/utf8>>
);
{option_of, Inner} ->
check_supported_field_type(Inner, <<Path/binary, ".inner"/utf8>>);
{result_of, Ok, Err} ->
lists:append(
check_supported_field_type(Ok, <<Path/binary, ".ok"/utf8>>),
check_supported_field_type(Err, <<Path/binary, ".err"/utf8>>)
);
{dict_of, Key, Value} ->
Key_errors = case Key of
int_field ->
[];
string_field ->
[];
bool_field ->
[];
_ ->
[{json_error,
Path,
<<<<"Dict key type "/utf8,
(libero@field_type:to_canonical_token(Key))/binary>>/binary,
" is not supported by JSON transport; use Int, String, or Bool keys"/utf8>>}]
end,
lists:append(
Key_errors,
check_supported_field_type(
Value,
<<Path/binary, ".value"/utf8>>
)
);
{tuple_of, Elements} ->
_pipe@2 = Elements,
_pipe@3 = gleam@list:index_map(
_pipe@2,
fun(Element@1, Index@2) -> {Element@1, Index@2} end
),
gleam@list:flat_map(
_pipe@3,
fun(Pair@1) ->
{Element@2, Index@3} = Pair@1,
check_supported_field_type(
Element@2,
<<<<<<Path/binary, ".element["/utf8>>/binary,
(erlang:integer_to_binary(Index@3))/binary>>/binary,
"]"/utf8>>
)
end
)
end.
-file("src/libero/json/codegen.gleam", 321).
-spec check_supported_field_types(list(libero@walker:discovered_type())) -> {ok,
nil} |
{error, list(libero@json@error:json_error())}.
check_supported_field_types(Discovered) ->
Errors = gleam@list:flat_map(
Discovered,
fun(Dt) ->
gleam@list:flat_map(
erlang:element(5, Dt),
fun(V) -> _pipe = erlang:element(7, V),
_pipe@1 = gleam@list:index_map(
_pipe,
fun(Field, Index) -> {Field, Index} end
),
gleam@list:flat_map(
_pipe@1,
fun(Pair) ->
{Field@1, Index@1} = Pair,
check_supported_field_type(
Field@1,
<<<<<<<<<<<<<<(erlang:element(2, Dt))/binary,
"."/utf8>>/binary,
(erlang:element(3, Dt))/binary>>/binary,
"."/utf8>>/binary,
(erlang:element(3, V))/binary>>/binary,
".field["/utf8>>/binary,
(erlang:integer_to_binary(Index@1))/binary>>/binary,
"]"/utf8>>
)
end
) end
)
end
),
case Errors of
[] ->
{ok, nil};
_ ->
{error, Errors}
end.
-file("src/libero/json/codegen.gleam", 315).
-spec has_mixed_fields(list(gleam@option:option(binary()))) -> boolean().
has_mixed_fields(Labels) ->
Has_labelled = gleam@list:any(Labels, fun(L) -> L /= none end),
Has_unlabelled = gleam@list:any(Labels, fun(L@1) -> L@1 =:= none end),
Has_labelled andalso Has_unlabelled.
-file("src/libero/json/codegen.gleam", 288).
-spec check_no_mixed_fields(list(libero@walker:discovered_type())) -> {ok, nil} |
{error, list(libero@json@error:json_error())}.
check_no_mixed_fields(Discovered) ->
Errors = gleam@list:flat_map(
Discovered,
fun(Dt) ->
gleam@list:flat_map(
erlang:element(5, Dt),
fun(V) -> case has_mixed_fields(erlang:element(6, V)) of
true ->
[{json_error,
<<<<<<<<(erlang:element(2, Dt))/binary,
"."/utf8>>/binary,
(erlang:element(3, Dt))/binary>>/binary,
"."/utf8>>/binary,
(erlang:element(3, V))/binary>>,
<<"mixed labelled/unlabelled fields are not supported in JSON v1"/utf8>>}];
false ->
[]
end end
)
end
),
case Errors of
[] ->
{ok, nil};
_ ->
{error, Errors}
end.
-file("src/libero/json/codegen.gleam", 49).
-spec generate_with_extra(
list(libero@walker:discovered_type()),
list(libero@field_type:field_type()),
binary(),
boolean(),
boolean()
) -> {ok, binary()} | {error, list(libero@json@error:json_error())}.
generate_with_extra(
Discovered,
Extra_fields,
Extra_tail,
Needs_json_wire,
Needs_frame
) ->
gleam@result:'try'(
check_no_mixed_fields(Discovered),
fun(_) ->
gleam@result:'try'(
check_supported_field_types(Discovered),
fun(_) ->
Aliases = begin
_pipe = Discovered,
_pipe@1 = gleam@list:map(
_pipe,
fun(Dt) -> erlang:element(2, Dt) end
),
libero@codegen:build_module_alias_map(_pipe@1)
end,
User_imports = begin
_pipe@2 = Aliases,
_pipe@3 = maps:to_list(_pipe@2),
_pipe@4 = gleam@list:filter(
_pipe@3,
fun(Pair) ->
{Mod, _} = Pair,
(not gleam_stdlib:string_starts_with(
Mod,
<<"gleam/"/utf8>>
)
andalso not gleam_stdlib:string_starts_with(
Mod,
<<"libero/"/utf8>>
))
andalso (Mod /= <<"gleam"/utf8>>)
end
),
_pipe@5 = gleam@list:map(
_pipe@4,
fun(Pair@1) ->
{Mod@1, Alias} = Pair@1,
Seg = libero@field_type:last_segment(Mod@1),
case Alias =:= Seg of
true ->
<<"import "/utf8, Mod@1/binary>>;
false ->
<<<<<<"import "/utf8, Mod@1/binary>>/binary,
" as "/utf8>>/binary,
Alias/binary>>
end
end
),
_pipe@6 = gleam@string:join(_pipe@5, <<"\n"/utf8>>),
(fun(S) -> case S of
<<""/utf8>> ->
<<""/utf8>>;
_ ->
<<S/binary, "\n"/utf8>>
end end)(_pipe@6)
end,
All_fields = lists:append(
collect_all_fields(Discovered),
Extra_fields
),
Needs_bit_array = gleam@list:any(
All_fields,
fun(Ft) -> Ft =:= bit_array_field end
),
Has_dict_field = gleam@list:any(All_fields, fun uses_dict/1),
Needs_dict = Has_dict_field,
Has_list_field = gleam@list:any(All_fields, fun uses_list/1),
Needs_list = Has_dict_field orelse Has_list_field,
Bit_array_import = case Needs_bit_array of
true ->
<<"import gleam/bit_array\n"/utf8>>;
false ->
<<""/utf8>>
end,
Dict_import = case Needs_dict of
true ->
<<"import gleam/dict\n"/utf8>>;
false ->
<<""/utf8>>
end,
Int_import = <<"import gleam/int\n"/utf8>>,
List_import = case Needs_list of
true ->
<<"import gleam/list\n"/utf8>>;
false ->
<<""/utf8>>
end,
List_at_helper = <<""/utf8>>,
Json_wire_import = case Needs_json_wire of
true ->
<<"import libero/json/wire as json_wire\n"/utf8>>;
false ->
<<""/utf8>>
end,
Frame_import = case Needs_frame of
true ->
<<"import libero/frame\n"/utf8>>;
false ->
<<""/utf8>>
end,
Header = <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"// Generated by libero. DO NOT EDIT.\n"/utf8,
"////\n"/utf8>>/binary,
"//// Typed JSON encoders and decoders for all discovered types.\n"/utf8>>/binary,
"\n"/utf8>>/binary,
"import gleam/dynamic.{type Dynamic}\n"/utf8>>/binary,
"import gleam/json\n"/utf8>>/binary,
Bit_array_import/binary>>/binary,
Frame_import/binary>>/binary,
Json_wire_import/binary>>/binary,
"import libero/json/runtime as json_runtime\n"/utf8>>/binary,
"import libero/json/error.{type JsonError, JsonError}\n"/utf8>>/binary,
"import gleam/result\n"/utf8>>/binary,
"import gleam/option.{type Option, None, Some}\n"/utf8>>/binary,
Int_import/binary>>/binary,
List_import/binary>>/binary,
Dict_import/binary>>/binary,
User_imports/binary>>/binary,
"\n"/utf8>>/binary,
List_at_helper/binary>>/binary,
"\n"/utf8>>,
Encoders = gleam@list:map(
Discovered,
fun(Dt@1) -> emit_type_encoder(Dt@1, Aliases) end
),
Decoders = gleam@list:map(
Discovered,
fun(Dt@2) -> emit_type_decoder(Dt@2, Aliases) end
),
Builtin_encoders = <<<<(emit_option_encoder())/binary,
"\n\n"/utf8>>/binary,
(emit_result_encoder())/binary>>,
Builtin_decoders = <<<<(emit_option_decoder())/binary,
"\n\n"/utf8>>/binary,
(emit_result_decoder())/binary>>,
{ok,
<<<<<<<<<<<<<<<<<<Header/binary,
(gleam@string:join(
Encoders,
<<"\n\n"/utf8>>
))/binary>>/binary,
"\n\n"/utf8>>/binary,
(gleam@string:join(
Decoders,
<<"\n\n"/utf8>>
))/binary>>/binary,
"\n\n"/utf8>>/binary,
Builtin_encoders/binary>>/binary,
"\n\n"/utf8>>/binary,
Builtin_decoders/binary>>/binary,
Extra_tail/binary>>/binary,
"\n"/utf8>>}
end
)
end
).
-file("src/libero/json/codegen.gleam", 37).
?DOC(
" Generate Gleam source for JSON typed encoders and decoders.\n"
" Returns `Ok(source)` on success, or `Error(List(JsonError))` if any\n"
" constructor has mixed labelled/unlabelled fields (rejected for JSON v1).\n"
).
-spec generate(list(libero@walker:discovered_type())) -> {ok, binary()} |
{error, list(libero@json@error:json_error())}.
generate(Discovered) ->
generate_with_extra(Discovered, [], <<""/utf8>>, false, false).
-file("src/libero/json/codegen.gleam", 190).
-spec has_client_context_push(list(libero@json@contract:push_contract())) -> boolean().
has_client_context_push(Push_types) ->
gleam@list:any(
Push_types,
fun(Push) ->
erlang:element(2, Push) =:= <<"__ClientContext__"/utf8>>
end
).
-file("src/libero/json/codegen.gleam", 259).
-spec emit_ssr_helpers(list(libero@json@contract:ssr_model_contract())) -> binary().
emit_ssr_helpers(Ssr_models) ->
_pipe = Ssr_models,
_pipe@1 = gleam@list:flat_map(
_pipe,
fun(Model) ->
Qual = libero@walker:qualified_atom_name(
erlang:element(3, Model),
erlang:element(4, Model)
),
[<<<<<<<<<<<<"pub fn json_encode_ssr_"/utf8, Qual/binary>>/binary,
"(value value) -> String {\n"/utf8>>/binary,
" json_wire.encode_flags(json_encode_"/utf8>>/binary,
Qual/binary>>/binary,
"(value))\n"/utf8>>/binary,
"}"/utf8>>,
<<<<<<<<<<<<"pub fn json_decode_ssr_"/utf8, Qual/binary>>/binary,
"(flags flags: String) {\n"/utf8>>/binary,
" json_wire.decode_flags_typed(flags:, decoder: json_decode_"/utf8>>/binary,
Qual/binary>>/binary,
")\n"/utf8>>/binary,
"}"/utf8>>]
end
),
_pipe@2 = gleam@list:unique(_pipe@1),
gleam@string:join(_pipe@2, <<"\n\n"/utf8>>).
-file("src/libero/json/codegen.gleam", 214).
-spec emit_client_context_helpers(list(libero@json@contract:push_contract())) -> binary().
emit_client_context_helpers(Push_types) ->
_pipe = Push_types,
_pipe@1 = gleam@list:filter(
_pipe,
fun(Push) ->
erlang:element(2, Push) =:= <<"__ClientContext__"/utf8>>
end
),
_pipe@2 = gleam@list:flat_map(
_pipe@1,
fun(Push@1) ->
Qual = libero@walker:qualified_atom_name(
erlang:element(3, Push@1),
erlang:element(4, Push@1)
),
[<<<<<<<<<<<<<<<<"pub fn json_encode_client_context_"/utf8,
Qual/binary>>/binary,
"(value value) -> String {\n"/utf8>>/binary,
" json_wire.encode_push(module: \""/utf8>>/binary,
"__ClientContext__"/utf8>>/binary,
"\", value: json_encode_"/utf8>>/binary,
Qual/binary>>/binary,
"(value))\n"/utf8>>/binary,
"}"/utf8>>,
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"pub fn json_decode_client_context_"/utf8,
Qual/binary>>/binary,
"(data data: String) {\n"/utf8>>/binary,
" case json_wire.decode_server_frame(data) {\n"/utf8>>/binary,
" Ok(frame.Push(module:, value: raw)) -> case module {\n"/utf8>>/binary,
" \""/utf8>>/binary,
"__ClientContext__"/utf8>>/binary,
"\" -> json_decode_"/utf8>>/binary,
Qual/binary>>/binary,
"(raw)\n"/utf8>>/binary,
" other -> Error([JsonError(\"module\", \"expected "/utf8>>/binary,
"__ClientContext__"/utf8>>/binary,
", got \" <> other)])\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" Ok(_) -> Error([JsonError(\"kind\", \"expected client-context push frame\")])\n"/utf8>>/binary,
" Error(errors) -> Error(errors)\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
"}"/utf8>>]
end
),
_pipe@3 = gleam@list:unique(_pipe@2),
gleam@string:join(_pipe@3, <<"\n\n"/utf8>>).
-file("src/libero/json/codegen.gleam", 194).
-spec emit_push_helpers(list(libero@json@contract:push_contract())) -> binary().
emit_push_helpers(Push_types) ->
_pipe = Push_types,
_pipe@1 = gleam@list:map(
_pipe,
fun(Push) ->
Qual = libero@walker:qualified_atom_name(
erlang:element(3, Push),
erlang:element(4, Push)
),
<<<<<<<<<<<<"pub fn json_encode_push_"/utf8, Qual/binary>>/binary,
"(module module: String, value value) -> String {\n"/utf8>>/binary,
" json_wire.encode_push(module:, value: json_encode_"/utf8>>/binary,
Qual/binary>>/binary,
"(value))\n"/utf8>>/binary,
"}"/utf8>>
end
),
_pipe@2 = gleam@list:unique(_pipe@1),
gleam@string:join(_pipe@2, <<"\n\n"/utf8>>).
-file("src/libero/json/codegen.gleam", 162).
?DOC(" Generate JSON codecs and transport wrappers for transport, push, and SSR.\n").
-spec generate_transport_codecs_with_push_and_ssr(
list(libero@walker:discovered_type()),
list(libero@json@contract:push_contract()),
list(libero@json@contract:ssr_model_contract())
) -> {ok, binary()} | {error, list(libero@json@error:json_error())}.
generate_transport_codecs_with_push_and_ssr(Discovered, Push_types, Ssr_models) ->
Transport_helpers = begin
_pipe = [emit_push_helpers(Push_types),
emit_client_context_helpers(Push_types),
emit_ssr_helpers(Ssr_models)],
_pipe@1 = gleam@list:filter(_pipe, fun(Src) -> Src /= <<""/utf8>> end),
_pipe@2 = gleam@string:join(_pipe@1, <<"\n\n"/utf8>>),
(fun(Src@1) -> case Src@1 of
<<""/utf8>> ->
<<""/utf8>>;
_ ->
<<"\n\n"/utf8, Src@1/binary>>
end end)(_pipe@2)
end,
generate_with_extra(
Discovered,
[],
Transport_helpers,
not gleam@list:is_empty(Push_types) orelse not gleam@list:is_empty(
Ssr_models
),
has_client_context_push(Push_types)
).