Current section

Files

Jump to
oaspec src oaspec@codegen@encoders.erl
Raw

src/oaspec@codegen@encoders.erl

-module(oaspec@codegen@encoders).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/oaspec/codegen/encoders.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(
" JSON encoder generation.\n"
"\n"
" Split out of `decoders.gleam` so each module owns one direction of\n"
" the codec pipeline — decoders produces `decode.gleam`, encoders\n"
" produces `encode.gleam`. Shared traversal helpers (`list_at_or`,\n"
" `qualified_schema_ref_type`) are intentionally duplicated rather\n"
" than lifted into a third module; the helpers are small, the\n"
" duplication is stable, and extracting a shared dispatch module is\n"
" tracked as follow-up to #212.\n"
).
-file("src/oaspec/codegen/encoders.gleam", 680).
?DOC(" Generate encoder for a primitive type (String, Int, Float, Bool) or Array.\n").
-spec generate_primitive_encoder(
gleam@string_tree:string_tree(),
binary(),
binary(),
binary(),
binary()
) -> gleam@string_tree:string_tree().
generate_primitive_encoder(Sb, Fn_name, Json_fn_name, Gleam_type, Json_expr) ->
_pipe = Sb,
_pipe@1 = oaspec@util@string_extra:line(
_pipe,
<<<<<<<<"pub fn "/utf8, Json_fn_name/binary>>/binary, "(value: "/utf8>>/binary,
Gleam_type/binary>>/binary,
") -> json.Json {"/utf8>>
),
_pipe@2 = oaspec@util@string_extra:indent(_pipe@1, 1, Json_expr),
_pipe@3 = oaspec@util@string_extra:line(_pipe@2, <<"}"/utf8>>),
_pipe@4 = oaspec@util@string_extra:blank_line(_pipe@3),
_pipe@5 = oaspec@util@string_extra:line(
_pipe@4,
<<<<<<<<"pub fn "/utf8, Fn_name/binary>>/binary, "(value: "/utf8>>/binary,
Gleam_type/binary>>/binary,
") -> String {"/utf8>>
),
_pipe@6 = oaspec@util@string_extra:indent(
_pipe@5,
1,
<<Json_fn_name/binary, "(value) |> json.to_string()"/utf8>>
),
_pipe@7 = oaspec@util@string_extra:line(_pipe@6, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@7).
-file("src/oaspec/codegen/encoders.gleam", 732).
?DOC(
" Convert a SchemaRef to a json.Json encoder function reference.\n"
" Used for json.nullable(value, <fn>) and json.array(list, <fn>).\n"
).
-spec schema_ref_to_json_encoder_fn(
oaspec@openapi@schema:schema_ref(),
binary(),
binary()
) -> binary().
schema_ref_to_json_encoder_fn(Ref, Parent_name, Prop_name) ->
case Ref of
{inline, {string_schema, _, _, Enum_values, _, _, _}} when Enum_values =/= [] ->
<<<<"encode_"/utf8,
(oaspec@util@naming:to_snake_case(
<<(oaspec@util@naming:schema_to_type_name(Parent_name))/binary,
(oaspec@util@naming:schema_to_type_name(Prop_name))/binary>>
))/binary>>/binary,
"_json"/utf8>>;
{inline, {array_schema, _, Items, _, _, _}} ->
Inner = schema_ref_to_json_encoder_fn(Items, Parent_name, Prop_name),
<<<<"fn(items) { json.array(items, "/utf8, Inner/binary>>/binary,
") }"/utf8>>;
_ ->
oaspec@codegen@schema_dispatch:json_encoder_fn(Ref)
end.
-file("src/oaspec/codegen/encoders.gleam", 704).
?DOC(
" Convert a SchemaRef to a json.Json encoder expression.\n"
" Returns an expression that produces json.Json (not String).\n"
).
-spec schema_ref_to_json_encoder(
binary(),
oaspec@openapi@schema:schema_ref(),
binary(),
binary()
) -> binary().
schema_ref_to_json_encoder(Value_expr, Ref, Parent_name, Prop_name) ->
case Ref of
{inline, {string_schema, _, _, Enum_values, _, _, _}} when Enum_values =/= [] ->
Fn_name = <<<<"encode_"/utf8,
(oaspec@util@naming:to_snake_case(
<<(oaspec@util@naming:schema_to_type_name(Parent_name))/binary,
(oaspec@util@naming:schema_to_type_name(Prop_name))/binary>>
))/binary>>/binary,
"_json"/utf8>>,
<<<<<<Fn_name/binary, "("/utf8>>/binary, Value_expr/binary>>/binary,
")"/utf8>>;
{inline, {array_schema, _, Items, _, _, _}} ->
Inner_fn = schema_ref_to_json_encoder_fn(
Items,
Parent_name,
Prop_name
),
<<<<<<<<"json.array("/utf8, Value_expr/binary>>/binary, ", "/utf8>>/binary,
Inner_fn/binary>>/binary,
")"/utf8>>;
_ ->
oaspec@codegen@schema_dispatch:json_encoder_expr(Ref, Value_expr)
end.
-file("src/oaspec/codegen/encoders.gleam", 755).
?DOC(" Get element at index from a list, or return a default.\n").
-spec list_at_or(list(binary()), integer(), binary()) -> binary().
list_at_or(Lst, Idx, Default) ->
case {Lst, Idx} of
{[], _} ->
Default;
{[Head | _], 0} ->
Head;
{[_ | Rest], N} ->
list_at_or(Rest, N - 1, Default)
end.
-file("src/oaspec/codegen/encoders.gleam", 764).
?DOC(" Convert a SchemaRef to a qualified Gleam type string (with types. prefix for refs).\n").
-spec qualified_schema_ref_type(
oaspec@openapi@schema:schema_ref(),
oaspec@codegen@context:context()
) -> binary().
qualified_schema_ref_type(Ref, Ctx) ->
case Ref of
{inline, Schema} ->
oaspec@codegen@types:schema_to_gleam_type(Schema, Ctx);
_ ->
oaspec@codegen@schema_dispatch:schema_ref_qualified_type(Ref)
end.
-file("src/oaspec/codegen/encoders.gleam", 251).
?DOC(
" Generate an encoder function for a schema.\n"
" Each type gets two functions:\n"
" encode_x_json(value) -> json.Json (for composition in objects)\n"
" encode_x(value) -> String (for standalone use)\n"
).
-spec generate_encoder(
gleam@string_tree:string_tree(),
binary(),
oaspec@openapi@schema:schema_ref(),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_encoder(Sb, Name, Schema_ref, Ctx) ->
Type_name = oaspec@util@naming:schema_to_type_name(Name),
Fn_name = <<"encode_"/utf8,
(oaspec@util@naming:to_snake_case(Name))/binary>>,
Json_fn_name = <<Fn_name/binary, "_json"/utf8>>,
case Schema_ref of
{inline,
{object_schema,
_,
Properties,
Required,
Additional_properties,
_,
_}} ->
Sb@1 = begin
_pipe = Sb,
oaspec@util@string_extra:line(
_pipe,
<<<<<<<<"pub fn "/utf8, Json_fn_name/binary>>/binary,
"(value: types."/utf8>>/binary,
Type_name/binary>>/binary,
") -> json.Json {"/utf8>>
)
end,
Has_ap = case Additional_properties of
{typed, _} ->
true;
untyped ->
true;
forbidden ->
false;
unspecified ->
false
end,
Sb@2 = case Has_ap of
true ->
_pipe@1 = Sb@1,
oaspec@util@string_extra:indent(
_pipe@1,
1,
<<"let base_props = ["/utf8>>
);
false ->
_pipe@2 = Sb@1,
oaspec@util@string_extra:indent(
_pipe@2,
1,
<<"json.object(["/utf8>>
)
end,
All_props = oaspec@codegen@ir_build:sorted_entries(Properties),
All_deduped_names = oaspec@openapi@dedup:dedup_property_names(
gleam@list:map(All_props, fun(E) -> erlang:element(1, E) end)
),
Props_with_names = begin
_pipe@3 = gleam@list:index_map(
All_props,
fun(Entry, Idx) ->
{Prop_name, Prop_ref} = Entry,
Field_name = list_at_or(
All_deduped_names,
Idx,
oaspec@util@naming:to_snake_case(Prop_name)
),
{Prop_name, Prop_ref, Field_name}
end
),
gleam@list:filter(
_pipe@3,
fun(Entry@1) ->
{_, Prop_ref@1, _} = Entry@1,
not oaspec@codegen@types:schema_ref_is_read_only(
Prop_ref@1,
Ctx
)
end
)
end,
Sb@4 = gleam@list:index_fold(
Props_with_names,
Sb@2,
fun(Sb@3, Entry@2, Idx@1) ->
{Prop_name@1, Prop_ref@2, Field_name@1} = Entry@2,
Is_required = gleam@list:contains(Required, Prop_name@1),
Trailing = case Idx@1 =:= (erlang:length(Props_with_names) - 1) of
true ->
<<""/utf8>>;
false ->
<<","/utf8>>
end,
Encoder_expr = schema_ref_to_json_encoder(
<<"value."/utf8, Field_name@1/binary>>,
Prop_ref@2,
Name,
Prop_name@1
),
case Is_required of
true ->
_pipe@4 = Sb@3,
oaspec@util@string_extra:indent(
_pipe@4,
2,
<<<<<<<<<<"#(\""/utf8, Prop_name@1/binary>>/binary,
"\", "/utf8>>/binary,
Encoder_expr/binary>>/binary,
")"/utf8>>/binary,
Trailing/binary>>
);
false ->
_pipe@5 = Sb@3,
oaspec@util@string_extra:indent(
_pipe@5,
2,
<<<<<<<<<<<<<<"#(\""/utf8, Prop_name@1/binary>>/binary,
"\", json.nullable(value."/utf8>>/binary,
Field_name@1/binary>>/binary,
", "/utf8>>/binary,
(schema_ref_to_json_encoder_fn(
Prop_ref@2,
Name,
Prop_name@1
))/binary>>/binary,
"))"/utf8>>/binary,
Trailing/binary>>
)
end
end
),
Sb@5 = case Additional_properties of
{typed, Ap_ref} ->
Inner_encoder_fn = schema_ref_to_json_encoder_fn(
Ap_ref,
Name,
<<"additional_properties"/utf8>>
),
_pipe@6 = Sb@4,
_pipe@7 = oaspec@util@string_extra:indent(
_pipe@6,
1,
<<"]"/utf8>>
),
_pipe@8 = oaspec@util@string_extra:indent(
_pipe@7,
1,
<<<<"let extra_props = dict.to_list(value.additional_properties) |> list.map(fn(entry) { let #(k, v) = entry; #(k, "/utf8,
Inner_encoder_fn/binary>>/binary,
"(v)) })"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@8,
1,
<<"json.object(list.append(base_props, extra_props))"/utf8>>
);
untyped ->
_pipe@9 = Sb@4,
_pipe@10 = oaspec@util@string_extra:indent(
_pipe@9,
1,
<<"]"/utf8>>
),
_pipe@11 = oaspec@util@string_extra:indent(
_pipe@10,
1,
<<"let extra_props = dict.to_list(value.additional_properties) |> list.map(fn(entry) { let #(k, v) = entry\n #(k, encode_dynamic(v)) })"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@11,
1,
<<"json.object(list.append(base_props, extra_props))"/utf8>>
);
forbidden ->
_pipe@12 = Sb@4,
oaspec@util@string_extra:indent(_pipe@12, 1, <<"])"/utf8>>);
unspecified ->
_pipe@12 = Sb@4,
oaspec@util@string_extra:indent(_pipe@12, 1, <<"])"/utf8>>)
end,
Sb@6 = begin
_pipe@13 = Sb@5,
_pipe@14 = oaspec@util@string_extra:line(_pipe@13, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@14)
end,
_pipe@15 = Sb@6,
_pipe@16 = oaspec@util@string_extra:line(
_pipe@15,
<<<<<<<<"pub fn "/utf8, Fn_name/binary>>/binary,
"(value: types."/utf8>>/binary,
Type_name/binary>>/binary,
") -> String {"/utf8>>
),
_pipe@17 = oaspec@util@string_extra:indent(
_pipe@16,
1,
<<Json_fn_name/binary, "(value) |> json.to_string()"/utf8>>
),
_pipe@18 = oaspec@util@string_extra:line(_pipe@17, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@18);
{inline, {string_schema, _, _, Enum_values, _, _, _}} when Enum_values =/= [] ->
Enc_deduped_variants = oaspec@openapi@dedup:dedup_enum_variants(
Enum_values
),
Sb@7 = begin
_pipe@19 = Sb,
_pipe@20 = oaspec@util@string_extra:line(
_pipe@19,
<<<<<<<<"pub fn "/utf8, Json_fn_name/binary>>/binary,
"(value: types."/utf8>>/binary,
Type_name/binary>>/binary,
") -> json.Json {"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@20,
1,
<<"let str = case value {"/utf8>>
)
end,
Sb@9 = gleam@list:index_fold(
Enum_values,
Sb@7,
fun(Sb@8, Value, Idx@2) ->
Variant_suffix = list_at_or(
Enc_deduped_variants,
Idx@2,
oaspec@util@naming:to_pascal_case(Value)
),
Variant = <<(oaspec@util@naming:schema_to_type_name(
Type_name
))/binary,
Variant_suffix/binary>>,
_pipe@21 = Sb@8,
oaspec@util@string_extra:indent(
_pipe@21,
2,
<<<<<<<<"types."/utf8, Variant/binary>>/binary,
" -> \""/utf8>>/binary,
Value/binary>>/binary,
"\""/utf8>>
)
end
),
Sb@10 = begin
_pipe@22 = Sb@9,
_pipe@23 = oaspec@util@string_extra:indent(
_pipe@22,
1,
<<"}"/utf8>>
),
_pipe@24 = oaspec@util@string_extra:indent(
_pipe@23,
1,
<<"json.string(str)"/utf8>>
),
_pipe@25 = oaspec@util@string_extra:line(_pipe@24, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@25)
end,
Sb@11 = begin
_pipe@26 = Sb@10,
_pipe@27 = oaspec@util@string_extra:line(
_pipe@26,
<<<<<<<<"pub fn "/utf8, Fn_name/binary>>/binary,
"(value: types."/utf8>>/binary,
Type_name/binary>>/binary,
") -> String {"/utf8>>
),
_pipe@28 = oaspec@util@string_extra:indent(
_pipe@27,
1,
<<Json_fn_name/binary, "(value) |> json.to_string()"/utf8>>
),
_pipe@29 = oaspec@util@string_extra:line(_pipe@28, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@29)
end,
To_string_fn_name = <<Fn_name/binary, "_to_string"/utf8>>,
Sb@12 = begin
_pipe@30 = Sb@11,
_pipe@31 = oaspec@util@string_extra:line(
_pipe@30,
<<<<<<<<"pub fn "/utf8, To_string_fn_name/binary>>/binary,
"(value: types."/utf8>>/binary,
Type_name/binary>>/binary,
") -> String {"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@31,
1,
<<"case value {"/utf8>>
)
end,
Sb@14 = gleam@list:index_fold(
Enum_values,
Sb@12,
fun(Sb@13, Value@1, Idx@3) ->
Variant_suffix@1 = list_at_or(
Enc_deduped_variants,
Idx@3,
oaspec@util@naming:to_pascal_case(Value@1)
),
Variant@1 = <<(oaspec@util@naming:schema_to_type_name(
Type_name
))/binary,
Variant_suffix@1/binary>>,
_pipe@32 = Sb@13,
oaspec@util@string_extra:indent(
_pipe@32,
2,
<<<<<<<<"types."/utf8, Variant@1/binary>>/binary,
" -> \""/utf8>>/binary,
Value@1/binary>>/binary,
"\""/utf8>>
)
end
),
_pipe@33 = Sb@14,
_pipe@34 = oaspec@util@string_extra:indent(
_pipe@33,
1,
<<"}"/utf8>>
),
_pipe@35 = oaspec@util@string_extra:line(_pipe@34, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@35);
{inline, {all_of_schema, Metadata, Schemas}} ->
Merged = oaspec@codegen@types:merge_allof_schemas(Schemas, Ctx),
Merged_schema = {inline,
{object_schema,
Metadata,
erlang:element(2, Merged),
erlang:element(3, Merged),
erlang:element(4, Merged),
none,
none}},
generate_encoder(Sb, Name, Merged_schema, Ctx);
{inline, {one_of_schema, _, Schemas@1, _}} ->
All_refs = gleam@list:all(Schemas@1, fun(S) -> case S of
{reference, _, _} ->
true;
_ ->
false
end end),
case All_refs of
false ->
Sb;
true ->
Sb@15 = begin
_pipe@36 = Sb,
_pipe@37 = oaspec@util@string_extra:line(
_pipe@36,
<<<<<<<<"pub fn "/utf8, Json_fn_name/binary>>/binary,
"(value: types."/utf8>>/binary,
Type_name/binary>>/binary,
") -> json.Json {"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@37,
1,
<<"case value {"/utf8>>
)
end,
Sb@17 = gleam@list:fold(
Schemas@1,
Sb@15,
fun(Sb@16, S_ref) -> case S_ref of
{reference, _, Name@1} ->
Variant_type = oaspec@util@naming:schema_to_type_name(
Name@1
),
Variant_name = <<Type_name/binary,
Variant_type/binary>>,
Inner_encoder = <<<<"encode_"/utf8,
(oaspec@util@naming:to_snake_case(
Name@1
))/binary>>/binary,
"_json"/utf8>>,
_pipe@38 = Sb@16,
oaspec@util@string_extra:indent(
_pipe@38,
2,
<<<<<<<<"types."/utf8,
Variant_name/binary>>/binary,
"(inner) -> "/utf8>>/binary,
Inner_encoder/binary>>/binary,
"(inner)"/utf8>>
);
_ ->
Sb@16
end end
),
Sb@18 = begin
_pipe@39 = Sb@17,
_pipe@40 = oaspec@util@string_extra:indent(
_pipe@39,
1,
<<"}"/utf8>>
),
_pipe@41 = oaspec@util@string_extra:line(
_pipe@40,
<<"}"/utf8>>
),
oaspec@util@string_extra:blank_line(_pipe@41)
end,
_pipe@42 = Sb@18,
_pipe@43 = oaspec@util@string_extra:line(
_pipe@42,
<<<<<<<<"pub fn "/utf8, Fn_name/binary>>/binary,
"(value: types."/utf8>>/binary,
Type_name/binary>>/binary,
") -> String {"/utf8>>
),
_pipe@44 = oaspec@util@string_extra:indent(
_pipe@43,
1,
<<Json_fn_name/binary,
"(value) |> json.to_string()"/utf8>>
),
_pipe@45 = oaspec@util@string_extra:line(
_pipe@44,
<<"}"/utf8>>
),
oaspec@util@string_extra:blank_line(_pipe@45)
end;
{inline, {any_of_schema, _, Schemas@2, _}} ->
All_refs@1 = gleam@list:all(Schemas@2, fun(S@1) -> case S@1 of
{reference, _, _} ->
true;
_ ->
false
end end),
case All_refs@1 of
false ->
Sb;
true ->
Variant_fields = gleam@list:map(
Schemas@2,
fun(S_ref@1) -> case S_ref@1 of
{reference, _, Name@2} ->
{oaspec@util@naming:to_snake_case(Name@2),
<<<<"encode_"/utf8,
(oaspec@util@naming:to_snake_case(
Name@2
))/binary>>/binary,
"_json"/utf8>>};
{inline, _} ->
{<<"unknown"/utf8>>, <<"json.null"/utf8>>}
end end
),
Sb@19 = begin
_pipe@46 = Sb,
oaspec@util@string_extra:line(
_pipe@46,
<<<<<<<<"pub fn "/utf8, Json_fn_name/binary>>/binary,
"(value: types."/utf8>>/binary,
Type_name/binary>>/binary,
") -> json.Json {"/utf8>>
)
end,
Sb@21 = gleam@list:fold(
Variant_fields,
Sb@19,
fun(Sb@20, Field) ->
{Field_name@2, Encoder_fn} = Field,
_pipe@47 = Sb@20,
_pipe@48 = oaspec@util@string_extra:indent(
_pipe@47,
1,
<<<<"case value."/utf8, Field_name@2/binary>>/binary,
" {"/utf8>>
),
_pipe@49 = oaspec@util@string_extra:indent(
_pipe@48,
2,
<<<<"option.Some(v) -> "/utf8,
Encoder_fn/binary>>/binary,
"(v)"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@49,
2,
<<"option.None ->"/utf8>>
)
end
),
Sb@22 = begin
_pipe@50 = Sb@21,
oaspec@util@string_extra:indent(
_pipe@50,
(erlang:length(Variant_fields) + 1),
<<"json.null()"/utf8>>
)
end,
Sb@24 = gleam@list:fold(
Variant_fields,
Sb@22,
fun(Sb@23, _) -> _pipe@51 = Sb@23,
oaspec@util@string_extra:indent(
_pipe@51,
1,
<<"}"/utf8>>
) end
),
Sb@25 = begin
_pipe@52 = Sb@24,
_pipe@53 = oaspec@util@string_extra:line(
_pipe@52,
<<"}"/utf8>>
),
oaspec@util@string_extra:blank_line(_pipe@53)
end,
_pipe@54 = Sb@25,
_pipe@55 = oaspec@util@string_extra:line(
_pipe@54,
<<<<<<<<"pub fn "/utf8, Fn_name/binary>>/binary,
"(value: types."/utf8>>/binary,
Type_name/binary>>/binary,
") -> String {"/utf8>>
),
_pipe@56 = oaspec@util@string_extra:indent(
_pipe@55,
1,
<<Json_fn_name/binary,
"(value) |> json.to_string()"/utf8>>
),
_pipe@57 = oaspec@util@string_extra:line(
_pipe@56,
<<"}"/utf8>>
),
oaspec@util@string_extra:blank_line(_pipe@57)
end;
{inline, {string_schema, Metadata@1, _, [], _, _, _}} ->
{Gleam_type, Json_expr} = case erlang:element(3, Metadata@1) of
true ->
{<<"Option(String)"/utf8>>,
<<"json.nullable(value, json.string)"/utf8>>};
false ->
{<<"String"/utf8>>, <<"json.string(value)"/utf8>>}
end,
generate_primitive_encoder(
Sb,
Fn_name,
Json_fn_name,
Gleam_type,
Json_expr
);
{inline, {integer_schema, Metadata@2, _, _, _, _, _, _}} ->
{Gleam_type@1, Json_expr@1} = case erlang:element(3, Metadata@2) of
true ->
{<<"Option(Int)"/utf8>>,
<<"json.nullable(value, json.int)"/utf8>>};
false ->
{<<"Int"/utf8>>, <<"json.int(value)"/utf8>>}
end,
generate_primitive_encoder(
Sb,
Fn_name,
Json_fn_name,
Gleam_type@1,
Json_expr@1
);
{inline, {number_schema, Metadata@3, _, _, _, _, _, _}} ->
{Gleam_type@2, Json_expr@2} = case erlang:element(3, Metadata@3) of
true ->
{<<"Option(Float)"/utf8>>,
<<"json.nullable(value, json.float)"/utf8>>};
false ->
{<<"Float"/utf8>>, <<"json.float(value)"/utf8>>}
end,
generate_primitive_encoder(
Sb,
Fn_name,
Json_fn_name,
Gleam_type@2,
Json_expr@2
);
{inline, {boolean_schema, Metadata@4}} ->
{Gleam_type@3, Json_expr@3} = case erlang:element(3, Metadata@4) of
true ->
{<<"Option(Bool)"/utf8>>,
<<"json.nullable(value, json.bool)"/utf8>>};
false ->
{<<"Bool"/utf8>>, <<"json.bool(value)"/utf8>>}
end,
generate_primitive_encoder(
Sb,
Fn_name,
Json_fn_name,
Gleam_type@3,
Json_expr@3
);
{inline, {array_schema, _, Items, _, _, _}} ->
Inner_type = qualified_schema_ref_type(Items, Ctx),
Gleam_type@4 = <<<<"List("/utf8, Inner_type/binary>>/binary,
")"/utf8>>,
Inner_encoder@1 = schema_ref_to_json_encoder_fn(
Items,
Name,
<<""/utf8>>
),
generate_primitive_encoder(
Sb,
Fn_name,
Json_fn_name,
Gleam_type@4,
<<<<"json.array(value, "/utf8, Inner_encoder@1/binary>>/binary,
")"/utf8>>
);
_ ->
Sb
end.
-file("src/oaspec/codegen/encoders.gleam", 176).
?DOC(" Generate inline enum encoders found in object/allOf properties.\n").
-spec generate_inline_enum_encoders(
gleam@string_tree:string_tree(),
binary(),
oaspec@openapi@schema:schema_ref(),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_inline_enum_encoders(Sb, Parent_name, Schema_ref, Ctx) ->
Props = case Schema_ref of
{inline, {object_schema, _, Properties, _, _, _, _}} ->
oaspec@codegen@ir_build:sorted_entries(Properties);
{inline, {all_of_schema, _, Schemas}} ->
oaspec@codegen@ir_build:sorted_entries(
erlang:element(
2,
oaspec@codegen@allof_merge:merge_allof_schemas(Schemas, Ctx)
)
);
_ ->
[]
end,
gleam@list:fold(
Props,
Sb,
fun(Sb@1, Entry) ->
{Prop_name, Prop_ref} = Entry,
case Prop_ref of
{inline, {string_schema, _, _, Enum_values, _, _, _}} when Enum_values =/= [] ->
Enum_name = <<(oaspec@util@naming:schema_to_type_name(
Parent_name
))/binary,
(oaspec@util@naming:schema_to_type_name(Prop_name))/binary>>,
generate_encoder(Sb@1, Enum_name, Prop_ref, Ctx);
_ ->
Sb@1
end
end
).
-file("src/oaspec/codegen/encoders.gleam", 218).
?DOC(" Generate encoder for an inline requestBody schema.\n").
-spec generate_anonymous_request_body_encoder(
gleam@string_tree:string_tree(),
binary(),
oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_anonymous_request_body_encoder(Sb, Op_id, Operation, Ctx) ->
case erlang:element(7, Operation) of
{some, {value, Rb}} ->
Content_entries = oaspec@codegen@ir_build:sorted_entries(
erlang:element(3, Rb)
),
case Content_entries of
[{_, Media_type} | _] ->
case erlang:element(2, Media_type) of
{some, {inline, Schema_obj}} ->
Filtered_schema = oaspec@codegen@types:filter_read_only_properties(
Schema_obj,
Ctx
),
Name = <<(oaspec@util@naming:to_snake_case(Op_id))/binary,
"_request_body"/utf8>>,
generate_encoder(
Sb,
Name,
{inline, Filtered_schema},
Ctx
);
_ ->
Sb
end;
_ ->
Sb
end;
{some, {ref, _}} ->
Sb;
none ->
Sb
end.
-file("src/oaspec/codegen/encoders.gleam", 205).
?DOC(" Generate encoders for anonymous inline schemas (response/requestBody).\n").
-spec generate_anonymous_encoders(
gleam@string_tree:string_tree(),
oaspec@codegen@context:context(),
list({binary(),
oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()),
binary(),
oaspec@openapi@spec:http_method()})
) -> gleam@string_tree:string_tree().
generate_anonymous_encoders(Sb, Ctx, Operations) ->
gleam@list:fold(
Operations,
Sb,
fun(Sb@1, Op) ->
{Op_id, Operation, _, _} = Op,
generate_anonymous_request_body_encoder(Sb@1, Op_id, Operation, Ctx)
end
).
-file("src/oaspec/codegen/encoders.gleam", 47).
?DOC(" Generate JSON encoders for all component schemas and anonymous types.\n").
-spec generate_encoders(
oaspec@codegen@context:context(),
list({binary(),
oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()),
binary(),
oaspec@openapi@spec:http_method()})
) -> binary().
generate_encoders(Ctx, Operations) ->
Schemas = case erlang:element(5, oaspec@codegen@context:spec(Ctx)) of
{some, Components} ->
_pipe = gleam@list:sort(
maps:to_list(erlang:element(2, Components)),
fun(A, B) ->
gleam@string:compare(
erlang:element(1, A),
erlang:element(1, B)
)
end
),
gleam@list:filter(
_pipe,
fun(Entry) ->
not oaspec@codegen@ir_build:is_internal_schema(
erlang:element(2, Entry)
)
end
);
none ->
[]
end,
Needs_types = gleam@list:any(
Schemas,
fun(Entry@1) ->
{_, Schema_ref} = Entry@1,
case Schema_ref of
{inline, {object_schema, _, _, _, _, _, _}} ->
true;
{inline, {all_of_schema, _, _}} ->
true;
{inline, {one_of_schema, _, _, _}} ->
true;
{inline, {any_of_schema, _, _, _}} ->
true;
{inline, {string_schema, _, _, Enum_values, _, _, _}} when Enum_values =/= [] ->
true;
_ ->
false
end
end
),
Needs_dict = gleam@list:any(
Schemas,
fun(Entry@2) ->
{_, Schema_ref@1} = Entry@2,
case Schema_ref@1 of
{inline, {object_schema, _, _, _, {typed, _}, _, _}} ->
true;
{inline, {object_schema, _, _, _, untyped, _, _}} ->
true;
_ ->
false
end
end
),
Needs_dynamic = gleam@list:any(
Schemas,
fun(Entry@3) ->
{_, Schema_ref@2} = Entry@3,
case Schema_ref@2 of
{inline, {object_schema, _, _, _, untyped, _, _}} ->
true;
_ ->
false
end
end
),
Base_imports = case {Needs_dict, Needs_dynamic} of
{true, true} ->
[<<"gleam/dict"/utf8>>,
<<"gleam/dynamic"/utf8>>,
<<"gleam/dynamic/decode"/utf8>>,
<<"gleam/json"/utf8>>,
<<"gleam/list"/utf8>>];
{true, false} ->
[<<"gleam/dict"/utf8>>,
<<"gleam/json"/utf8>>,
<<"gleam/list"/utf8>>];
{_, _} ->
[<<"gleam/json"/utf8>>]
end,
Imports = case Needs_types of
true ->
lists:append(
Base_imports,
[<<(oaspec@config:package(oaspec@codegen@context:config(Ctx)))/binary,
"/types"/utf8>>]
);
false ->
Base_imports
end,
Sb = begin
_pipe@1 = oaspec@util@string_extra:file_header(<<"0.22.0"/utf8>>),
oaspec@util@string_extra:imports(_pipe@1, Imports)
end,
Sb@2 = gleam@list:fold(
Schemas,
Sb,
fun(Sb@1, Entry@4) ->
{Name, Schema_ref@3} = Entry@4,
generate_inline_enum_encoders(Sb@1, Name, Schema_ref@3, Ctx)
end
),
Sb@4 = gleam@list:fold(
Schemas,
Sb@2,
fun(Sb@3, Entry@5) ->
{Name@1, Schema_ref@4} = Entry@5,
generate_encoder(Sb@3, Name@1, Schema_ref@4, Ctx)
end
),
Sb@5 = generate_anonymous_encoders(Sb@4, Ctx, Operations),
Sb@6 = case Needs_dynamic of
true ->
_pipe@2 = Sb@5,
_pipe@3 = oaspec@util@string_extra:doc_comment(
_pipe@2,
<<"Encode a Dynamic value to JSON by inspecting its runtime type."/utf8>>
),
_pipe@4 = oaspec@util@string_extra:line(
_pipe@3,
<<"fn encode_dynamic(value: dynamic.Dynamic) -> json.Json {"/utf8>>
),
_pipe@5 = oaspec@util@string_extra:indent(
_pipe@4,
1,
<<"case dynamic.classify(value) {"/utf8>>
),
_pipe@6 = oaspec@util@string_extra:indent(
_pipe@5,
2,
<<"\"String\" -> {"/utf8>>
),
_pipe@7 = oaspec@util@string_extra:indent(
_pipe@6,
3,
<<"let assert Ok(s) = decode.run(value, decode.string)"/utf8>>
),
_pipe@8 = oaspec@util@string_extra:indent(
_pipe@7,
3,
<<"json.string(s)"/utf8>>
),
_pipe@9 = oaspec@util@string_extra:indent(_pipe@8, 2, <<"}"/utf8>>),
_pipe@10 = oaspec@util@string_extra:indent(
_pipe@9,
2,
<<"\"Int\" -> {"/utf8>>
),
_pipe@11 = oaspec@util@string_extra:indent(
_pipe@10,
3,
<<"let assert Ok(i) = decode.run(value, decode.int)"/utf8>>
),
_pipe@12 = oaspec@util@string_extra:indent(
_pipe@11,
3,
<<"json.int(i)"/utf8>>
),
_pipe@13 = oaspec@util@string_extra:indent(
_pipe@12,
2,
<<"}"/utf8>>
),
_pipe@14 = oaspec@util@string_extra:indent(
_pipe@13,
2,
<<"\"Float\" -> {"/utf8>>
),
_pipe@15 = oaspec@util@string_extra:indent(
_pipe@14,
3,
<<"let assert Ok(f) = decode.run(value, decode.float)"/utf8>>
),
_pipe@16 = oaspec@util@string_extra:indent(
_pipe@15,
3,
<<"json.float(f)"/utf8>>
),
_pipe@17 = oaspec@util@string_extra:indent(
_pipe@16,
2,
<<"}"/utf8>>
),
_pipe@18 = oaspec@util@string_extra:indent(
_pipe@17,
2,
<<"\"Bool\" -> {"/utf8>>
),
_pipe@19 = oaspec@util@string_extra:indent(
_pipe@18,
3,
<<"let assert Ok(b) = decode.run(value, decode.bool)"/utf8>>
),
_pipe@20 = oaspec@util@string_extra:indent(
_pipe@19,
3,
<<"json.bool(b)"/utf8>>
),
_pipe@21 = oaspec@util@string_extra:indent(
_pipe@20,
2,
<<"}"/utf8>>
),
_pipe@22 = oaspec@util@string_extra:indent(
_pipe@21,
2,
<<"\"Nil\" -> json.null()"/utf8>>
),
_pipe@23 = oaspec@util@string_extra:indent(
_pipe@22,
2,
<<"_ -> json.null()"/utf8>>
),
_pipe@24 = oaspec@util@string_extra:indent(
_pipe@23,
1,
<<"}"/utf8>>
),
_pipe@25 = oaspec@util@string_extra:line(_pipe@24, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@25);
false ->
Sb@5
end,
oaspec@util@string_extra:to_string(Sb@6).
-file("src/oaspec/codegen/encoders.gleam", 33).
?DOC(" Generate the `encode.gleam` module for the resolved spec.\n").
-spec generate(oaspec@codegen@context:context()) -> list(oaspec@codegen@context:generated_file()).
generate(Ctx) ->
Operations = oaspec@openapi@operations:collect_operations(Ctx),
Content = generate_encoders(Ctx, Operations),
[{generated_file,
<<"encode.gleam"/utf8>>,
Content,
shared_target,
overwrite}].