Packages
oaspec
0.23.0
0.68.0
0.67.0
0.66.0
0.65.0
0.64.0
0.63.0
0.62.0
0.61.0
0.60.0
0.59.0
0.58.1
0.58.0
0.57.0
0.56.0
0.55.0
0.54.0
0.53.0
0.52.0
0.51.0
0.50.0
0.49.0
0.48.0
0.47.0
0.46.0
0.45.0
0.44.0
0.43.0
0.42.0
0.41.0
0.40.0
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.0
0.30.0
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.3
0.6.1
0.6.0
0.5.0
0.4.0
0.3.0
0.1.3
Generate Gleam code from OpenAPI 3.x specifications
Current section
Files
Jump to
Current section
Files
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", 695).
?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", 747).
?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", 719).
?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", 772).
?DOC(
" Check if a SchemaRef is nullable (inline schemas only — references are\n"
" assumed non-nullable here since the ref target's nullability is baked\n"
" into the generated type elsewhere).\n"
).
-spec schema_ref_is_nullable(oaspec@openapi@schema:schema_ref()) -> boolean().
schema_ref_is_nullable(Ref) ->
case Ref of
{inline, Inline_schema} ->
oaspec@openapi@schema:is_nullable(Inline_schema);
{reference, _, _} ->
false
end.
-file("src/oaspec/codegen/encoders.gleam", 780).
?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", 789).
?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
),
Is_nullable = schema_ref_is_nullable(Prop_ref@2),
case {Is_required, Is_nullable} of
{true, false} ->
_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>>
);
{_, _} ->
_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 ->
erlang:error(#{gleam_error => panic,
message => (<<<<<<"oaspec: oneOf schema '"/utf8,
Name/binary>>/binary,
"' contains inline variant(s) which are not supported for encoder generation. "/utf8>>/binary,
"Move all oneOf variants to components/schemas and use $ref instead."/utf8>>),
file => <<?FILEPATH/utf8>>,
module => <<"oaspec/codegen/encoders"/utf8>>,
function => <<"generate_encoder"/utf8>>,
line => 491});
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 ->
erlang:error(#{gleam_error => panic,
message => (<<<<<<"oaspec: anyOf schema '"/utf8,
Name/binary>>/binary,
"' contains inline variant(s) which are not supported for encoder generation. "/utf8>>/binary,
"Move all anyOf variants to components/schemas and use $ref instead."/utf8>>),
file => <<?FILEPATH/utf8>>,
module => <<"oaspec/codegen/encoders"/utf8>>,
function => <<"generate_encoder"/utf8>>,
line => 560});
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.23.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}].