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]).
-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-RPC-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", 909).
?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 use _ <- result.try(\n case decode.run(value, decode.field(\"type\", decode.string, fn(x) { decode.success(x) })) {\n Ok(\"gleam/result.Result\") -> Ok(Nil)\n Ok(s) -> Error([JsonError(\"type\", \"expected gleam/result.Result, got \" <> s)])\n Error(_) -> Error([JsonError(\"type\", \"missing or not a string\")])\n }\n )\n case decode.run(value, decode.field(\"variant\", decode.string, fn(x) { decode.success(x) })) {\n Ok(\"Ok\") -> {\n case decode.run(value, decode.field(\"fields\", decode.list(of: decode.dynamic), fn(x) { decode.success(x) })) {\n Ok([inner_raw]) -> {\n use val <- result.try(decode_ok(inner_raw))\n Ok(Ok(val))\n }\n Ok(_) -> Error([JsonError(\"fields\", \"expected [value] for Ok\")])\n Error(_) -> Error([JsonError(\"fields\", \"expected array for Ok\")])\n }\n }\n Ok(\"Error\") -> {\n case decode.run(value, decode.field(\"fields\", decode.list(of: decode.dynamic), fn(x) { decode.success(x) })) {\n Ok([inner_raw]) -> {\n use val <- result.try(decode_err(inner_raw))\n Ok(Error(val))\n }\n Ok(_) -> Error([JsonError(\"fields\", \"expected [value] for Error\")])\n Error(_) -> Error([JsonError(\"fields\", \"expected array for Error\")])\n }\n }\n Ok(other) -> Error([JsonError(\"variant\", \"unknown Result variant: \" <> other)])\n Error(_) -> Error([JsonError(\"variant\", \"missing or not a string\")])\n }\n}"/utf8>>.
-file("src/libero/json/codegen.gleam", 899).
?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 use _ <- result.try(\n case decode.run(value, decode.field(\"type\", decode.string, fn(x) { decode.success(x) })) {\n Ok(\"gleam/option.Option\") -> Ok(Nil)\n Ok(s) -> Error([JsonError(\"type\", \"expected gleam/option.Option, got \" <> s)])\n Error(_) -> Error([JsonError(\"type\", \"missing or not a string\")])\n }\n )\n case decode.run(value, decode.field(\"variant\", decode.string, fn(x) { decode.success(x) })) {\n Ok(\"None\") -> Ok(None)\n Ok(\"Some\") -> {\n case decode.run(value, decode.field(\"fields\", decode.list(of: decode.dynamic), fn(x) { decode.success(x) })) {\n Ok([inner_raw]) -> {\n use val <- result.try(decode_inner(inner_raw))\n Ok(Some(val))\n }\n Ok(_) -> Error([JsonError(\"fields\", \"expected [value] for Some\")])\n Error(_) -> Error([JsonError(\"fields\", \"expected array for Some\")])\n }\n }\n Ok(other) -> Error([JsonError(\"variant\", \"unknown Option variant: \" <> other)])\n Error(_) -> Error([JsonError(\"variant\", \"missing or not a string\")])\n }\n}"/utf8>>.
-file("src/libero/json/codegen.gleam", 904).
?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 case value {\n Ok(inner) -> json.object([\n #(\"type\", json.string(\"gleam/result.Result\")),\n #(\"variant\", json.string(\"Ok\")),\n #(\"fields\", json.array([encode_ok(inner)], of: fn(x) { x })),\n ])\n Error(inner) -> json.object([\n #(\"type\", json.string(\"gleam/result.Result\")),\n #(\"variant\", json.string(\"Error\")),\n #(\"fields\", json.array([encode_err(inner)], of: fn(x) { x })),\n ])\n }\n}"/utf8>>.
-file("src/libero/json/codegen.gleam", 894).
?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 case value {\n Some(inner) -> json.object([\n #(\"type\", json.string(\"gleam/option.Option\")),\n #(\"variant\", json.string(\"Some\")),\n #(\"fields\", json.array([encode_inner(inner)], of: fn(x) { x })),\n ])\n None -> json.object([\n #(\"type\", json.string(\"gleam/option.Option\")),\n #(\"variant\", json.string(\"None\")),\n #(\"fields\", json.object([])),\n ])\n }\n}"/utf8>>.
-file("src/libero/json/codegen.gleam", 669).
?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", 657).
?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", 1024).
?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 list_at(arr, "/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(_) -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
"["/utf8>>/binary,
Idx_str/binary>>/binary,
"\", \"missing\")])\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 decode.run("/utf8>>/binary,
Raw_var/binary>>/binary,
", decode.list(of: decode.dynamic)) {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(arr) -> case list.length(arr) {\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(_) -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
"\", \"expected Array\")])\n"/utf8>>/binary,
Pad/binary>>/binary,
"}"/utf8>>.
-file("src/libero/json/codegen.gleam", 914).
?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 decode.run("/utf8>>/binary,
Raw_var/binary>>/binary,
", decode.dict(decode.string, decode.dynamic)) {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(entries) -> dict.fold(entries, Ok(dict.new()), fn(acc, k, v_raw) {\n"/utf8>>/binary,
Pad/binary>>/binary,
" use acc_dict <- result.try(acc)\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(dict.insert(acc_dict, k, v))\n"/utf8>>/binary,
Pad/binary>>/binary,
" })\n"/utf8>>/binary,
Pad/binary>>/binary,
" Error(_) -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
"\", \"expected Dict\")])\n"/utf8>>/binary,
Pad/binary>>/binary,
"}"/utf8>>;
_ ->
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Pad/binary,
"case decode.run("/utf8>>/binary,
Raw_var/binary>>/binary,
", decode.list(of: decode.dynamic)) {\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,
" case decode.run(pair_raw, decode.list(of: decode.dynamic)) {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok([k_raw, v_raw]) -> {\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,
" Ok(_) -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
"\", \"expected [key, value] pair\")])\n"/utf8>>/binary,
Pad/binary>>/binary,
" Error(_) -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
"\", \"expected Array pair\")])\n"/utf8>>/binary,
Pad/binary>>/binary,
" }\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(_) -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
"\", \"expected Array of pairs\")])\n"/utf8>>/binary,
Pad/binary>>/binary,
"}"/utf8>>
end.
-file("src/libero/json/codegen.gleam", 676).
?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, "case decode.run("/utf8>>/binary,
Raw_var/binary>>/binary,
", decode.string) {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(v) -> Ok(v)\n"/utf8>>/binary,
Pad/binary>>/binary,
" Error(_) -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
"\", \"expected String\")])\n"/utf8>>/binary,
Pad/binary>>/binary,
"}"/utf8>>;
int_field ->
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Pad/binary,
"case decode.run("/utf8>>/binary,
Raw_var/binary>>/binary,
", decode.int) {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(v) -> case v >= -9007199254740991 && v <= 9007199254740991 {\n"/utf8>>/binary,
Pad/binary>>/binary,
" True -> Ok(v)\n"/utf8>>/binary,
Pad/binary>>/binary,
" False -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
"\", \"expected Int in safe JSON range\")])\n"/utf8>>/binary,
Pad/binary>>/binary,
" }\n"/utf8>>/binary,
Pad/binary>>/binary,
" Error(_) -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
"\", \"expected Int\")])\n"/utf8>>/binary,
Pad/binary>>/binary,
"}"/utf8>>;
float_field ->
<<<<<<<<<<<<<<<<<<<<<<Pad/binary, "case decode.run("/utf8>>/binary,
Raw_var/binary>>/binary,
", decode.float) {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(v) -> Ok(v)\n"/utf8>>/binary,
Pad/binary>>/binary,
" Error(_) -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
"\", \"expected Float\")])\n"/utf8>>/binary,
Pad/binary>>/binary,
"}"/utf8>>;
bool_field ->
<<<<<<<<<<<<<<<<<<<<<<Pad/binary, "case decode.run("/utf8>>/binary,
Raw_var/binary>>/binary,
", decode.bool) {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(v) -> Ok(v)\n"/utf8>>/binary,
Pad/binary>>/binary,
" Error(_) -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
"\", \"expected Bool\")])\n"/utf8>>/binary,
Pad/binary>>/binary,
"}"/utf8>>;
nil_field ->
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Pad/binary, "case decode.run("/utf8>>/binary,
Raw_var/binary>>/binary,
", decode.optional(decode.dynamic)) {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(None) -> Ok(Nil)\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(Some(_)) -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
"\", \"expected null\")])\n"/utf8>>/binary,
Pad/binary>>/binary,
" Error(_) -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
"\", \"expected null\")])\n"/utf8>>/binary,
Pad/binary>>/binary,
"}"/utf8>>;
bit_array_field ->
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Pad/binary,
"case decode.run("/utf8>>/binary,
Raw_var/binary>>/binary,
", decode.string) {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(s) -> case bit_array.base64_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,
"\", \"expected valid base64 BitArray\")])\n"/utf8>>/binary,
Pad/binary>>/binary,
" }\n"/utf8>>/binary,
Pad/binary>>/binary,
" Error(_) -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
"\", \"expected String (base64 BitArray)\")])\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 decode.run("/utf8>>/binary,
Raw_var/binary>>/binary,
", decode.list(of: decode.dynamic)) {\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(_) -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
"\", \"expected Array\")])\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", 589).
?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 decode.run(fields, decode.list(of: decode.dynamic)) {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(a) -> case list.length(a) {\n"/utf8>>/binary,
Pad/binary>>/binary,
" "/utf8>>/binary,
N_str/binary>>/binary,
" -> Ok(a)\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(_) -> Error([JsonError(\"fields\", \"expected Array\")])\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 list_at(arr, "/utf8>>/binary,
Idx_str/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(_) -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
"\", \"missing\")])\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", 544).
?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 decode.run(fields, decode.field(\""/utf8>>/binary,
Name/binary>>/binary,
"\", decode.dynamic, fn(x) { decode.success(x) })) {\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(_) -> Error([JsonError(\""/utf8>>/binary,
Path/binary>>/binary,
"\", \"missing\")])\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", 484).
?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,
"\") -> Ok("/utf8>>/binary,
Alias/binary>>/binary,
"."/utf8>>/binary,
(erlang:element(3, V))/binary>>/binary,
")\n"/utf8>>;
_ ->
All_labelled = gleam@list:all(
erlang:element(6, V),
fun(L) -> L /= none end
),
Fields_extract = <<<<<<<<<<<<<<<<<<<<<<Pad/binary,
" use fields <- result.try(\n"/utf8>>/binary,
Pad/binary>>/binary,
" case decode.run(value, decode.field(\"fields\", decode.dynamic, fn(x) { decode.success(x) })) {\n"/utf8>>/binary,
Pad/binary>>/binary,
" Error(_) -> Error([JsonError(\"fields\", \"missing\")])\n"/utf8>>/binary,
Pad/binary>>/binary,
" Ok(f) -> Ok(f)\n"/utf8>>/binary,
Pad/binary>>/binary,
" }\n"/utf8>>/binary,
Pad/binary>>/binary,
" )\n"/utf8>>,
Field_decodes = case All_labelled of
true ->
emit_labelled_field_decodes(V, <<Pad/binary, " "/utf8>>);
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", 443).
?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 decode.run(value, decode.field(\"type\", decode.string, fn(x) { decode.success(x) })) {\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 decode.run(value, decode.field(\"variant\", decode.string, fn(x) { decode.success(x) })) {\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", 224).
?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, " *. 0.0 == 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", 208).
?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", 237).
?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.string(bit_array.base64_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", 416).
-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", 403).
-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", 390).
?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", 349).
?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(_, I) -> <<"f"/utf8, (erlang:integer_to_binary(I))/binary>> 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", 321).
?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", 1143).
-spec uses_list_or_tuple(libero@field_type:field_type()) -> boolean().
uses_list_or_tuple(Ft) ->
case Ft of
{list_of, _} ->
true;
{tuple_of, _} ->
true;
_ ->
false
end.
-file("src/libero/json/codegen.gleam", 1150).
-spec has_unlabelled_fields(list(libero@walker:discovered_type())) -> boolean().
has_unlabelled_fields(Discovered) ->
gleam@list:any(
Discovered,
fun(Dt) ->
gleam@list:any(
erlang:element(5, Dt),
fun(V) ->
gleam@list:any(
erlang:element(6, V),
fun(L) -> L =:= none end
)
end
)
end
).
-file("src/libero/json/codegen.gleam", 1136).
-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", 1119).
-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", 1111).
-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", 178).
?DOC(
" Build a map from module path to import alias for all modules referenced\n"
" by discovered types. Uses the last `/`-separated segment as the alias.\n"
" When two modules share the same last segment, the full underscored path\n"
" is used for both to avoid collisions.\n"
).
-spec build_module_alias_map(list(libero@walker:discovered_type())) -> gleam@dict:dict(binary(), binary()).
build_module_alias_map(Discovered) ->
Modules = begin
_pipe = Discovered,
_pipe@1 = gleam@list:map(_pipe, fun(Dt) -> erlang:element(2, Dt) end),
gleam@list:unique(_pipe@1)
end,
Segment_counts = gleam@list:fold(
Modules,
maps:new(),
fun(Acc, Mod) ->
Seg = libero@field_type:last_segment(Mod),
Count = case gleam_stdlib:map_get(Acc, Seg) of
{ok, N} ->
N + 1;
{error, nil} ->
1
end,
gleam@dict:insert(Acc, Seg, Count)
end
),
gleam@list:fold(
Modules,
maps:new(),
fun(Acc@1, Mod@1) ->
Seg@1 = libero@field_type:last_segment(Mod@1),
Alias = case gleam_stdlib:map_get(Segment_counts, Seg@1) of
{ok, N@1} when N@1 > 1 ->
gleam@string:replace(Mod@1, <<"/"/utf8>>, <<"_"/utf8>>);
_ ->
Seg@1
end,
gleam@dict:insert(Acc@1, Mod@1, Alias)
end
).
-file("src/libero/json/codegen.gleam", 168).
-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", 141).
-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", 33).
?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) ->
gleam@result:'try'(
check_no_mixed_fields(Discovered),
fun(_) ->
Aliases = build_module_alias_map(Discovered),
User_imports = begin
_pipe = Aliases,
_pipe@1 = maps:to_list(_pipe),
_pipe@2 = gleam@list:filter(
_pipe@1,
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@3 = gleam@list:map(
_pipe@2,
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@4 = gleam@string:join(_pipe@3, <<"\n"/utf8>>),
(fun(S) -> case S of
<<""/utf8>> ->
<<""/utf8>>;
_ ->
<<S/binary, "\n"/utf8>>
end end)(_pipe@4)
end,
All_fields = collect_all_fields(Discovered),
Needs_bit_array = gleam@list:any(
All_fields,
fun(Ft) -> Ft =:= bit_array_field end
),
Needs_dict = gleam@list:any(All_fields, fun uses_dict/1),
Needs_list_at = has_unlabelled_fields(Discovered) orelse gleam@list:any(
All_fields,
fun(Ft@1) -> case Ft@1 of
{tuple_of, _} ->
true;
_ ->
false
end end
),
Needs_list_or_int = (Needs_list_at orelse Needs_dict) orelse gleam@list:any(
All_fields,
fun uses_list_or_tuple/1
),
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 = case Needs_list_or_int of
true ->
<<"import gleam/int\n"/utf8>>;
false ->
<<""/utf8>>
end,
List_import = case Needs_list_or_int of
true ->
<<"import gleam/list\n"/utf8>>;
false ->
<<""/utf8>>
end,
List_at_helper = case Needs_list_at of
true ->
<<"/// Index into a list, returning Error if out of bounds.\nfn list_at(items: List(a), index: Int) -> Result(a, List(JsonError)) {\n case list.drop(items, index) |> list.first {\n Ok(item) -> Ok(item)\n Error(_) -> Error([JsonError(\"fields[\" <> int.to_string(index) <> \"]\", \"missing\")])\n }\n}\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/dynamic/decode\n"/utf8>>/binary,
"import gleam/json\n"/utf8>>/binary,
Bit_array_import/binary>>/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) -> emit_type_encoder(Dt, Aliases) end
),
Decoders = gleam@list:map(
Discovered,
fun(Dt@1) -> emit_type_decoder(Dt@1, 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,
"\n"/utf8>>}
end
).