Packages
oaspec
0.25.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@decoders.erl
-module(oaspec@codegen@decoders).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/oaspec/codegen/decoders.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.
-file("src/oaspec/codegen/decoders.gleam", 829).
?DOC(" Generate decoder for a primitive type (String, Int, Float, Bool).\n").
-spec generate_primitive_decoder(
gleam@string_tree:string_tree(),
binary(),
binary(),
binary(),
binary()
) -> gleam@string_tree:string_tree().
generate_primitive_decoder(
Sb,
Fn_name,
Decoder_fn_name,
Gleam_type,
Decoder_expr
) ->
_pipe = Sb,
_pipe@1 = oaspec@util@string_extra:line(
_pipe,
<<<<<<<<"pub fn "/utf8, Decoder_fn_name/binary>>/binary,
"() -> decode.Decoder("/utf8>>/binary,
Gleam_type/binary>>/binary,
") {"/utf8>>
),
_pipe@2 = oaspec@util@string_extra:indent(_pipe@1, 1, Decoder_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,
"(json_string: String) -> Result("/utf8>>/binary,
Gleam_type/binary>>/binary,
", json.DecodeError) {"/utf8>>
),
_pipe@6 = oaspec@util@string_extra:indent(
_pipe@5,
1,
<<<<"json.parse(json_string, "/utf8, Decoder_expr/binary>>/binary,
")"/utf8>>
),
_pipe@7 = oaspec@util@string_extra:line(_pipe@6, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@7).
-file("src/oaspec/codegen/decoders.gleam", 1151).
?DOC(
" Get the discriminator value for a $ref.\n"
" OpenAPI discriminator.mapping is keyed by payload values, with $ref paths\n"
" as values: { \"dog\": \"#/components/schemas/Dog\" }.\n"
" Given a ref_name like \"Dog\", find the mapping key that points to it.\n"
" Falls back to ref_name if no explicit mapping exists.\n"
).
-spec get_discriminator_value(
oaspec@openapi@schema:discriminator(),
binary(),
binary()
) -> binary().
get_discriminator_value(Disc, Ref, Ref_name) ->
Found = begin
_pipe = maps:to_list(erlang:element(3, Disc)),
gleam@list:find(
_pipe,
fun(Entry) ->
{_, Target} = Entry,
(Target =:= Ref) orelse (oaspec@openapi@resolver:ref_to_name(
Target
)
=:= Ref_name)
end
)
end,
case Found of
{ok, {Disc_value, _}} ->
Disc_value;
{error, _} ->
Ref_name
end.
-file("src/oaspec/codegen/decoders.gleam", 1173).
?DOC(
" Convert a SchemaRef to a decoder expression string.\n"
" parent_name is used to resolve inline enum decoder names.\n"
).
-spec schema_ref_to_decoder(
oaspec@openapi@schema:schema_ref(),
binary(),
binary()
) -> binary().
schema_ref_to_decoder(Ref, Parent_name, Prop_name) ->
case Ref of
{inline, {string_schema, _, _, Enum_values, _, _, _}} when Enum_values =/= [] ->
Decoder_name = 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>>
),
<<Decoder_name/binary, "_decoder()"/utf8>>;
{inline, {array_schema, _, Items, _, _, _}} ->
Inner = schema_ref_to_decoder(Items, Parent_name, Prop_name),
<<<<"decode.list("/utf8, Inner/binary>>/binary, ")"/utf8>>;
_ ->
oaspec@codegen@schema_dispatch:decoder_expr(Ref)
end.
-file("src/oaspec/codegen/decoders.gleam", 1197).
?DOC(" Check if a SchemaRef has nullable: true.\n").
-spec schema_ref_is_nullable(
oaspec@openapi@schema:schema_ref(),
oaspec@codegen@context:context()
) -> boolean().
schema_ref_is_nullable(Ref, Ctx) ->
case Ref of
{inline, Schema} ->
oaspec@openapi@schema:is_nullable(Schema);
{reference, _, _} ->
case oaspec@openapi@resolver:resolve_schema_ref(
Ref,
oaspec@codegen@context:spec(Ctx)
) of
{ok, Resolved_schema} ->
oaspec@openapi@schema:is_nullable(Resolved_schema);
{error, _} ->
false
end
end.
-file("src/oaspec/codegen/decoders.gleam", 1210).
?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/decoders.gleam", 1219).
?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/decoders.gleam", 860).
?DOC(" Generate decoder for an ArraySchema.\n").
-spec generate_array_decoder(
gleam@string_tree:string_tree(),
binary(),
binary(),
binary(),
oaspec@openapi@schema:schema_ref(),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_array_decoder(Sb, Name, Fn_name, Decoder_fn_name, Items, Ctx) ->
Inner_decoder = schema_ref_to_decoder(Items, Name, <<""/utf8>>),
Inner_type = qualified_schema_ref_type(Items, Ctx),
Gleam_type = <<<<"List("/utf8, Inner_type/binary>>/binary, ")"/utf8>>,
_pipe = Sb,
_pipe@1 = oaspec@util@string_extra:line(
_pipe,
<<<<<<<<"pub fn "/utf8, Decoder_fn_name/binary>>/binary,
"() -> decode.Decoder("/utf8>>/binary,
Gleam_type/binary>>/binary,
") {"/utf8>>
),
_pipe@2 = oaspec@util@string_extra:indent(
_pipe@1,
1,
<<<<"decode.list("/utf8, Inner_decoder/binary>>/binary, ")"/utf8>>
),
_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,
"(json_string: String) -> Result("/utf8>>/binary,
Gleam_type/binary>>/binary,
", json.DecodeError) {"/utf8>>
),
_pipe@6 = oaspec@util@string_extra:indent(
_pipe@5,
1,
<<<<"json.parse(json_string, "/utf8, Decoder_fn_name/binary>>/binary,
"())"/utf8>>
),
_pipe@7 = oaspec@util@string_extra:line(_pipe@6, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@7).
-file("src/oaspec/codegen/decoders.gleam", 1227).
?DOC(" Add a doc comment if description is present.\n").
-spec maybe_doc_comment(
gleam@string_tree:string_tree(),
gleam@option:option(binary())
) -> gleam@string_tree:string_tree().
maybe_doc_comment(Sb, Description) ->
case Description of
{some, Desc} ->
_pipe = Sb,
oaspec@util@string_extra:doc_comment(_pipe, Desc);
none ->
Sb
end.
-file("src/oaspec/codegen/decoders.gleam", 681).
?DOC(" Generate decoder for a string enum schema.\n").
-spec generate_enum_decoder(
gleam@string_tree:string_tree(),
binary(),
binary(),
binary(),
gleam@option:option(binary()),
list(binary())
) -> gleam@string_tree:string_tree().
generate_enum_decoder(
Sb,
Type_name,
Fn_name,
Decoder_fn_name,
Description,
Enum_values
) ->
Sb@1 = maybe_doc_comment(Sb, Description),
Sb@2 = begin
_pipe = Sb@1,
_pipe@1 = oaspec@util@string_extra:line(
_pipe,
<<<<<<<<"pub fn "/utf8, Decoder_fn_name/binary>>/binary,
"() -> decode.Decoder(types."/utf8>>/binary,
Type_name/binary>>/binary,
") {"/utf8>>
),
_pipe@2 = oaspec@util@string_extra:indent(
_pipe@1,
1,
<<"use value <- decode.then(decode.string)"/utf8>>
),
oaspec@util@string_extra:indent(_pipe@2, 1, <<"case value {"/utf8>>)
end,
Deduped_variants = oaspec@openapi@dedup:dedup_enum_variants(Enum_values),
Sb@4 = gleam@list:index_fold(
Enum_values,
Sb@2,
fun(Sb@3, Value, Idx) ->
Variant_suffix = list_at_or(
Deduped_variants,
Idx,
oaspec@util@naming:to_pascal_case(Value)
),
Variant = <<(oaspec@util@naming:schema_to_type_name(Type_name))/binary,
Variant_suffix/binary>>,
_pipe@3 = Sb@3,
oaspec@util@string_extra:indent(
_pipe@3,
2,
<<<<<<<<"\""/utf8, Value/binary>>/binary,
"\" -> decode.success(types."/utf8>>/binary,
Variant/binary>>/binary,
")"/utf8>>
)
end
),
First_variant_suffix = list_at_or(Deduped_variants, 0, case Enum_values of
[First | _] ->
oaspec@util@naming:to_pascal_case(First);
[] ->
<<"Unknown"/utf8>>
end),
_pipe@4 = Sb@4,
_pipe@5 = oaspec@util@string_extra:indent(
_pipe@4,
2,
<<<<<<<<<<"_ -> decode.failure(types."/utf8,
(oaspec@util@naming:schema_to_type_name(Type_name))/binary>>/binary,
First_variant_suffix/binary>>/binary,
", \""/utf8>>/binary,
Type_name/binary>>/binary,
": unknown variant \" <> value)"/utf8>>
),
_pipe@6 = oaspec@util@string_extra:indent(_pipe@5, 1, <<"}"/utf8>>),
_pipe@7 = oaspec@util@string_extra:line(_pipe@6, <<"}"/utf8>>),
_pipe@8 = oaspec@util@string_extra:blank_line(_pipe@7),
_pipe@9 = oaspec@util@string_extra:line(
_pipe@8,
<<<<<<<<"pub fn "/utf8, Fn_name/binary>>/binary,
"(json_string: String) -> Result(types."/utf8>>/binary,
Type_name/binary>>/binary,
", json.DecodeError) {"/utf8>>
),
_pipe@10 = oaspec@util@string_extra:indent(
_pipe@9,
1,
<<<<"json.parse(json_string, "/utf8, Decoder_fn_name/binary>>/binary,
"())"/utf8>>
),
_pipe@11 = oaspec@util@string_extra:line(_pipe@10, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@11).
-file("src/oaspec/codegen/decoders.gleam", 748).
?DOC(" Generate decoder for an anyOf (inclusive union) schema.\n").
-spec generate_anyof_decoder(
gleam@string_tree:string_tree(),
binary(),
binary(),
binary(),
gleam@option:option(binary()),
list(oaspec@openapi@schema:schema_ref())
) -> gleam@string_tree:string_tree().
generate_anyof_decoder(
Sb,
Type_name,
Fn_name,
Decoder_fn_name,
Description,
Schemas
) ->
Sb@1 = maybe_doc_comment(Sb, Description),
Variant_fields = gleam@list:map(Schemas, fun(S_ref) -> case S_ref of
{reference, _, Ref_name} ->
Field_name = oaspec@util@naming:to_snake_case(Ref_name),
Decoder_name = <<<<"decode_"/utf8,
(oaspec@util@naming:to_snake_case(Ref_name))/binary>>/binary,
"_decoder"/utf8>>,
{Field_name, Decoder_name, Ref_name};
{inline, _} ->
{<<"unknown"/utf8>>,
<<"decode.string"/utf8>>,
<<"Unknown"/utf8>>}
end end),
Sb@2 = begin
_pipe = Sb@1,
oaspec@util@string_extra:line(
_pipe,
<<<<<<<<"pub fn "/utf8, Decoder_fn_name/binary>>/binary,
"() -> decode.Decoder(types."/utf8>>/binary,
Type_name/binary>>/binary,
") {"/utf8>>
)
end,
Sb@4 = gleam@list:fold(
Variant_fields,
Sb@2,
fun(Sb@3, Field) ->
{Field_name@1, Decoder_name@1, _} = Field,
_pipe@1 = Sb@3,
oaspec@util@string_extra:indent(
_pipe@1,
1,
<<<<<<<<"use "/utf8, Field_name@1/binary>>/binary,
" <- decode.then(decode.one_of("/utf8>>/binary,
Decoder_name@1/binary>>/binary,
"() |> decode.map(option.Some), or: [decode.success(option.None)]))"/utf8>>
)
end
),
Field_assignments = gleam@list:map(
Variant_fields,
fun(F) ->
{Field_name@2, _, _} = F,
<<<<Field_name@2/binary, ": "/utf8>>/binary, Field_name@2/binary>>
end
),
_pipe@2 = Sb@4,
_pipe@3 = oaspec@util@string_extra:indent(
_pipe@2,
1,
<<<<<<<<"decode.success(types."/utf8, Type_name/binary>>/binary,
"("/utf8>>/binary,
(gleam@string:join(Field_assignments, <<", "/utf8>>))/binary>>/binary,
"))"/utf8>>
),
_pipe@4 = oaspec@util@string_extra:line(_pipe@3, <<"}"/utf8>>),
_pipe@5 = oaspec@util@string_extra:blank_line(_pipe@4),
_pipe@6 = oaspec@util@string_extra:line(
_pipe@5,
<<<<<<<<"pub fn "/utf8, Fn_name/binary>>/binary,
"(json_string: String) -> Result(types."/utf8>>/binary,
Type_name/binary>>/binary,
", json.DecodeError) {"/utf8>>
),
_pipe@7 = oaspec@util@string_extra:indent(
_pipe@6,
1,
<<<<"json.parse(json_string, "/utf8, Decoder_fn_name/binary>>/binary,
"())"/utf8>>
),
_pipe@8 = oaspec@util@string_extra:line(_pipe@7, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@8).
-file("src/oaspec/codegen/decoders.gleam", 897).
?DOC(
" Generate decoder for oneOf/anyOf union types.\n"
" With discriminator: decode based on discriminator field value.\n"
" Without discriminator: try each variant decoder in order.\n"
).
-spec generate_oneof_decoder(
gleam@string_tree:string_tree(),
binary(),
binary(),
binary(),
binary(),
gleam@option:option(binary()),
list(oaspec@openapi@schema:schema_ref()),
gleam@option:option(oaspec@openapi@schema:discriminator()),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_oneof_decoder(
Sb,
_,
Type_name,
Fn_name,
Decoder_fn_name,
Description,
Schemas,
Discriminator,
_
) ->
All_refs = gleam@list:all(Schemas, fun(S) -> case S of
{reference, _, _} ->
true;
_ ->
false
end end),
case All_refs of
false ->
Sb;
true ->
Sb@1 = maybe_doc_comment(Sb, Description),
case Discriminator of
{some, Disc} ->
Sb@2 = begin
_pipe = Sb@1,
_pipe@1 = oaspec@util@string_extra:line(
_pipe,
<<<<<<<<"pub fn "/utf8, Decoder_fn_name/binary>>/binary,
"() -> decode.Decoder(types."/utf8>>/binary,
Type_name/binary>>/binary,
") {"/utf8>>
),
_pipe@2 = oaspec@util@string_extra:indent(
_pipe@1,
1,
<<<<"use disc_value <- decode.field(\""/utf8,
(erlang:element(2, Disc))/binary>>/binary,
"\", decode.string)"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@2,
1,
<<"case disc_value {"/utf8>>
)
end,
Sb@4 = gleam@list:fold(
Schemas,
Sb@2,
fun(Sb@3, S_ref) -> case S_ref of
{reference, Ref, Name} ->
Ref_name = Name,
Variant_type = oaspec@util@naming:schema_to_type_name(
Ref_name
),
Variant_name = <<Type_name/binary,
Variant_type/binary>>,
Variant_decoder = <<(oaspec@util@naming:to_snake_case(
Ref_name
))/binary,
"_decoder()"/utf8>>,
Disc_value = get_discriminator_value(
Disc,
Ref,
Ref_name
),
_pipe@3 = Sb@3,
_pipe@4 = oaspec@util@string_extra:indent(
_pipe@3,
2,
<<<<"\""/utf8, Disc_value/binary>>/binary,
"\" -> {"/utf8>>
),
_pipe@5 = oaspec@util@string_extra:indent(
_pipe@4,
3,
<<<<"use inner <- decode.then("/utf8,
Variant_decoder/binary>>/binary,
")"/utf8>>
),
_pipe@6 = oaspec@util@string_extra:indent(
_pipe@5,
3,
<<<<"decode.success(types."/utf8,
Variant_name/binary>>/binary,
"(inner))"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@6,
2,
<<"}"/utf8>>
);
_ ->
Sb@3
end end
),
First_ref_decoder = case Schemas of
[{reference, _, Name@1} | _] ->
Ref_name@1 = Name@1,
<<(oaspec@util@naming:to_snake_case(Ref_name@1))/binary,
"_decoder()"/utf8>>;
_ ->
<<"decode.string"/utf8>>
end,
First_variant_name = case Schemas of
[{reference, _, Name@2} | _] ->
Ref_name@2 = Name@2,
Variant_type@1 = oaspec@util@naming:schema_to_type_name(
Ref_name@2
),
<<<<"types."/utf8, Type_name/binary>>/binary,
Variant_type@1/binary>>;
_ ->
<<"types."/utf8, Type_name/binary>>
end,
Valid_disc_values = begin
_pipe@7 = Schemas,
_pipe@8 = gleam@list:filter_map(
_pipe@7,
fun(S_ref@1) -> case S_ref@1 of
{reference, Ref@1, Name@3} ->
{ok,
get_discriminator_value(
Disc,
Ref@1,
Name@3
)};
_ ->
{error, nil}
end end
),
_pipe@9 = gleam@list:sort(
_pipe@8,
fun gleam@string:compare/2
),
gleam@string:join(_pipe@9, <<"|"/utf8>>)
end,
Sb@5 = begin
_pipe@10 = Sb@4,
_pipe@11 = oaspec@util@string_extra:indent(
_pipe@10,
2,
<<"_ -> {"/utf8>>
),
_pipe@12 = oaspec@util@string_extra:indent(
_pipe@11,
3,
<<<<<<<<"use _ <- decode.then(decode.failure(Nil, \""/utf8,
Type_name/binary>>/binary,
": unknown discriminator '\" <> disc_value <> \"' (expected "/utf8>>/binary,
Valid_disc_values/binary>>/binary,
")\"))"/utf8>>
),
_pipe@13 = oaspec@util@string_extra:indent(
_pipe@12,
3,
<<<<"use v <- decode.then("/utf8,
First_ref_decoder/binary>>/binary,
")"/utf8>>
),
_pipe@14 = oaspec@util@string_extra:indent(
_pipe@13,
3,
<<<<<<<<"decode.failure("/utf8,
First_variant_name/binary>>/binary,
"(v), \""/utf8>>/binary,
Type_name/binary>>/binary,
"\")"/utf8>>
),
_pipe@15 = oaspec@util@string_extra:indent(
_pipe@14,
2,
<<"}"/utf8>>
),
_pipe@16 = oaspec@util@string_extra:indent(
_pipe@15,
1,
<<"}"/utf8>>
),
_pipe@17 = oaspec@util@string_extra:line(
_pipe@16,
<<"}"/utf8>>
),
oaspec@util@string_extra:blank_line(_pipe@17)
end,
_pipe@18 = Sb@5,
_pipe@19 = oaspec@util@string_extra:line(
_pipe@18,
<<<<<<<<"pub fn "/utf8, Fn_name/binary>>/binary,
"(json_string: String) -> Result(types."/utf8>>/binary,
Type_name/binary>>/binary,
", json.DecodeError) {"/utf8>>
),
_pipe@20 = oaspec@util@string_extra:indent(
_pipe@19,
1,
<<<<"json.parse(json_string, "/utf8,
Decoder_fn_name/binary>>/binary,
"())"/utf8>>
),
_pipe@21 = oaspec@util@string_extra:line(
_pipe@20,
<<"}"/utf8>>
),
oaspec@util@string_extra:blank_line(_pipe@21);
none ->
Ref_variants = gleam@list:filter_map(
Schemas,
fun(S_ref@2) -> case S_ref@2 of
{reference, _, Name@4} ->
Ref_name@3 = Name@4,
{ok, Ref_name@3};
_ ->
{error, nil}
end end
),
Sb@6 = begin
_pipe@22 = Sb@1,
oaspec@util@string_extra:line(
_pipe@22,
<<<<<<<<"pub fn "/utf8, Decoder_fn_name/binary>>/binary,
"() -> decode.Decoder(types."/utf8>>/binary,
Type_name/binary>>/binary,
") {"/utf8>>
)
end,
Sb@10 = case Ref_variants of
[] ->
_pipe@23 = Sb@6,
oaspec@util@string_extra:indent(
_pipe@23,
1,
<<<<<<<<"decode.failure(types."/utf8,
Type_name/binary>>/binary,
", \""/utf8>>/binary,
Type_name/binary>>/binary,
"\")"/utf8>>
);
[First | Rest] ->
First_variant_type = oaspec@util@naming:schema_to_type_name(
First
),
First_decoder = <<(oaspec@util@naming:to_snake_case(
First
))/binary,
"_decoder()"/utf8>>,
Sb@7 = begin
_pipe@24 = Sb@6,
oaspec@util@string_extra:indent(
_pipe@24,
1,
<<<<<<<<<<"decode.one_of("/utf8,
First_decoder/binary>>/binary,
" |> decode.map(types."/utf8>>/binary,
Type_name/binary>>/binary,
First_variant_type/binary>>/binary,
"), ["/utf8>>
)
end,
Sb@9 = gleam@list:fold(
Rest,
Sb@7,
fun(Sb@8, Ref_name@4) ->
Variant_type@2 = oaspec@util@naming:schema_to_type_name(
Ref_name@4
),
Decoder = <<(oaspec@util@naming:to_snake_case(
Ref_name@4
))/binary,
"_decoder()"/utf8>>,
_pipe@25 = Sb@8,
oaspec@util@string_extra:indent(
_pipe@25,
2,
<<<<<<<<Decoder/binary,
" |> decode.map(types."/utf8>>/binary,
Type_name/binary>>/binary,
Variant_type@2/binary>>/binary,
"),"/utf8>>
)
end
),
_pipe@26 = Sb@9,
oaspec@util@string_extra:indent(
_pipe@26,
1,
<<"])"/utf8>>
)
end,
Sb@11 = begin
_pipe@27 = Sb@10,
_pipe@28 = oaspec@util@string_extra:line(
_pipe@27,
<<"}"/utf8>>
),
oaspec@util@string_extra:blank_line(_pipe@28)
end,
_pipe@29 = Sb@11,
_pipe@30 = oaspec@util@string_extra:line(
_pipe@29,
<<<<<<<<"pub fn "/utf8, Fn_name/binary>>/binary,
"(json_string: String) -> Result(types."/utf8>>/binary,
Type_name/binary>>/binary,
", json.DecodeError) {"/utf8>>
),
_pipe@31 = oaspec@util@string_extra:indent(
_pipe@30,
1,
<<<<"json.parse(json_string, "/utf8,
Decoder_fn_name/binary>>/binary,
"())"/utf8>>
),
_pipe@32 = oaspec@util@string_extra:line(
_pipe@31,
<<"}"/utf8>>
),
oaspec@util@string_extra:blank_line(_pipe@32)
end
end.
-file("src/oaspec/codegen/decoders.gleam", 1277).
?DOC(
" Escape a spec-derived string so it can be safely interpolated\n"
" inside a generated Gleam string literal. Mirrors the helper in\n"
" `encoders.gleam` — extracted here to keep the decoder module\n"
" self-contained for issue #309.\n"
).
-spec escape_for_string_literal(binary()) -> binary().
escape_for_string_literal(Value) ->
_pipe = Value,
_pipe@1 = gleam@string:replace(_pipe, <<"\\"/utf8>>, <<"\\\\"/utf8>>),
gleam@string:replace(_pipe@1, <<"\""/utf8>>, <<"\\\""/utf8>>).
-file("src/oaspec/codegen/decoders.gleam", 1246).
?DOC(
" Emit the decoder snippet for a constant (issue #309) field.\n"
"\n"
" The wire value is required and fully determined at codegen time\n"
" (an inline `enum: [<single>]`). The decoder reads it as a string\n"
" and chains a `decode.then` that fails fast on any other value.\n"
" `_` discards the binding because the surrounding record has no\n"
" slot for the field. Failing on mismatch keeps the discriminator\n"
" contract honest — silently accepting `kind: \"media\"` where only\n"
" `kind: \"text\"` is legal would defeat the purpose of the enum.\n"
).
-spec emit_constant_field_decoder(
gleam@string_tree:string_tree(),
binary(),
binary()
) -> gleam@string_tree:string_tree().
emit_constant_field_decoder(Sb, Prop_name, Constant_value) ->
Escaped = escape_for_string_literal(Constant_value),
_pipe = Sb,
_pipe@1 = oaspec@util@string_extra:indent(
_pipe,
1,
<<<<"use _ <- decode.field(\""/utf8, Prop_name/binary>>/binary,
"\", decode.then(decode.string, fn(constant_value) {"/utf8>>
),
_pipe@2 = oaspec@util@string_extra:indent(
_pipe@1,
2,
<<"case constant_value {"/utf8>>
),
_pipe@3 = oaspec@util@string_extra:indent(
_pipe@2,
3,
<<<<"\""/utf8, Escaped/binary>>/binary,
"\" -> decode.success(Nil)"/utf8>>
),
_pipe@4 = oaspec@util@string_extra:indent(
_pipe@3,
3,
<<<<<<<<"other -> decode.failure(Nil, \"expected "/utf8,
Prop_name/binary>>/binary,
"=\\\""/utf8>>/binary,
Escaped/binary>>/binary,
"\\\", got \\\"\" <> other <> \"\\\"\")"/utf8>>
),
_pipe@5 = oaspec@util@string_extra:indent(_pipe@4, 2, <<"}"/utf8>>),
oaspec@util@string_extra:indent(_pipe@5, 1, <<"}))"/utf8>>).
-file("src/oaspec/codegen/decoders.gleam", 421).
?DOC(" Generate decoder for an ObjectSchema (properties, required, additionalProperties).\n").
-spec generate_object_decoder(
gleam@string_tree:string_tree(),
binary(),
binary(),
binary(),
binary(),
gleam@option:option(binary()),
gleam@dict:dict(binary(), oaspec@openapi@schema:schema_ref()),
list(binary()),
oaspec@openapi@schema:additional_properties(),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_object_decoder(
Sb,
Name,
Type_name,
Fn_name,
Decoder_fn_name,
Description,
Properties,
Required,
Additional_properties,
Ctx
) ->
Sb@1 = maybe_doc_comment(Sb, Description),
Sb@2 = begin
_pipe = Sb@1,
oaspec@util@string_extra:line(
_pipe,
<<<<<<<<"pub fn "/utf8, Decoder_fn_name/binary>>/binary,
"() -> decode.Decoder(types."/utf8>>/binary,
Type_name/binary>>/binary,
") {"/utf8>>
)
end,
Props = oaspec@codegen@ir_build:sorted_entries(Properties),
Deduped_names = oaspec@openapi@dedup:dedup_property_names(
gleam@list:map(Props, fun(E) -> erlang:element(1, E) end)
),
Sb@4 = gleam@list:index_fold(
Props,
Sb@2,
fun(Sb@3, Entry, Idx) ->
{Prop_name, Prop_ref} = Entry,
Field_name = list_at_or(
Deduped_names,
Idx,
oaspec@util@naming:to_snake_case(Prop_name)
),
Is_write_only = oaspec@codegen@types:schema_ref_is_write_only(
Prop_ref,
Ctx
),
Is_required = gleam@list:contains(Required, Prop_name) andalso not Is_write_only,
Field_decoder = schema_ref_to_decoder(Prop_ref, Name, Prop_name),
Is_nullable_schema = schema_ref_is_nullable(Prop_ref, Ctx),
Effective_decoder = case Is_nullable_schema of
true ->
<<<<"decode.optional("/utf8, Field_decoder/binary>>/binary,
")"/utf8>>;
false ->
Field_decoder
end,
case oaspec@codegen@schema_utils:constant_property_value(
Prop_ref,
Prop_name,
Required
) of
{some, Constant_value} ->
_pipe@1 = Sb@3,
emit_constant_field_decoder(
_pipe@1,
Prop_name,
Constant_value
);
none ->
case Is_required of
true ->
_pipe@2 = Sb@3,
oaspec@util@string_extra:indent(
_pipe@2,
1,
<<<<<<<<<<<<"use "/utf8, Field_name/binary>>/binary,
" <- decode.field(\""/utf8>>/binary,
Prop_name/binary>>/binary,
"\", "/utf8>>/binary,
Effective_decoder/binary>>/binary,
")"/utf8>>
);
false ->
case Is_nullable_schema of
true ->
_pipe@3 = Sb@3,
oaspec@util@string_extra:indent(
_pipe@3,
1,
<<<<<<<<<<<<"use "/utf8,
Field_name/binary>>/binary,
" <- decode.optional_field(\""/utf8>>/binary,
Prop_name/binary>>/binary,
"\", option.None, "/utf8>>/binary,
Effective_decoder/binary>>/binary,
")"/utf8>>
);
false ->
_pipe@4 = Sb@3,
oaspec@util@string_extra:indent(
_pipe@4,
1,
<<<<<<<<<<<<"use "/utf8,
Field_name/binary>>/binary,
" <- decode.optional_field(\""/utf8>>/binary,
Prop_name/binary>>/binary,
"\", option.None, decode.optional("/utf8>>/binary,
Field_decoder/binary>>/binary,
"))"/utf8>>
)
end
end
end
end
),
Known_keys_expr = case gleam@list:is_empty(Props) of
true ->
<<"[]"/utf8>>;
false ->
<<<<"["/utf8,
(oaspec@util@string_extra:join_with(
gleam@list:map(
Props,
fun(Entry@1) ->
{Prop_name@1, _} = Entry@1,
<<<<"\""/utf8, Prop_name@1/binary>>/binary,
"\""/utf8>>
end
),
<<", "/utf8>>
))/binary>>/binary,
"]"/utf8>>
end,
Sb@5 = case Additional_properties of
{typed, Ap_ref} ->
Inner_decoder = schema_ref_to_decoder(
Ap_ref,
Name,
<<"additional_properties"/utf8>>
),
_pipe@5 = Sb@4,
_pipe@6 = oaspec@util@string_extra:indent(
_pipe@5,
1,
<<"use all_props <- decode.then(decode.dict(decode.string, decode.new_primitive_decoder(\"Dynamic\", fn(x) { Ok(x) })))"/utf8>>
),
_pipe@7 = oaspec@util@string_extra:indent(
_pipe@6,
1,
<<<<"let extra_props = dict.drop(all_props, "/utf8,
Known_keys_expr/binary>>/binary,
")"/utf8>>
),
_pipe@8 = oaspec@util@string_extra:indent(
_pipe@7,
1,
<<"let additional_properties_result = dict.fold(extra_props, Ok(dict.new()), fn(acc, k, v) {"/utf8>>
),
_pipe@9 = oaspec@util@string_extra:indent(
_pipe@8,
2,
<<"case acc {"/utf8>>
),
_pipe@10 = oaspec@util@string_extra:indent(
_pipe@9,
3,
<<"Ok(decoded_acc) ->"/utf8>>
),
_pipe@11 = oaspec@util@string_extra:indent(
_pipe@10,
4,
<<<<"case decode.run(v, "/utf8, Inner_decoder/binary>>/binary,
") {"/utf8>>
),
_pipe@12 = oaspec@util@string_extra:indent(
_pipe@11,
5,
<<"Ok(decoded) -> Ok(dict.insert(decoded_acc, k, decoded))"/utf8>>
),
_pipe@13 = oaspec@util@string_extra:indent(
_pipe@12,
5,
<<"Error(_) -> Error(Nil)"/utf8>>
),
_pipe@14 = oaspec@util@string_extra:indent(
_pipe@13,
4,
<<"}"/utf8>>
),
_pipe@15 = oaspec@util@string_extra:indent(
_pipe@14,
3,
<<"Error(_) -> Error(Nil)"/utf8>>
),
_pipe@16 = oaspec@util@string_extra:indent(
_pipe@15,
2,
<<"}"/utf8>>
),
_pipe@17 = oaspec@util@string_extra:indent(
_pipe@16,
1,
<<"})"/utf8>>
),
_pipe@18 = oaspec@util@string_extra:indent(
_pipe@17,
1,
<<"use additional_properties <- decode.then(case additional_properties_result {"/utf8>>
),
_pipe@19 = oaspec@util@string_extra:indent(
_pipe@18,
2,
<<"Ok(decoded) -> decode.success(decoded)"/utf8>>
),
_pipe@20 = oaspec@util@string_extra:indent(
_pipe@19,
2,
<<"Error(_) -> decode.failure(dict.new(), \"additionalProperties\")"/utf8>>
),
oaspec@util@string_extra:indent(_pipe@20, 1, <<"})"/utf8>>);
untyped ->
_pipe@21 = Sb@4,
_pipe@22 = oaspec@util@string_extra:indent(
_pipe@21,
1,
<<"use all_props <- decode.then(decode.dict(decode.string, decode.new_primitive_decoder(\"Dynamic\", fn(x) { Ok(x) })))"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@22,
1,
<<<<"let additional_properties = dict.drop(all_props, "/utf8,
Known_keys_expr/binary>>/binary,
")"/utf8>>
);
forbidden ->
Sb@4;
unspecified ->
Sb@4
end,
Param_names = begin
_pipe@23 = gleam@list:index_map(
Props,
fun(Entry@2, Idx@1) ->
{Prop_name@2, Prop_ref@1} = Entry@2,
Field_name@1 = list_at_or(
Deduped_names,
Idx@1,
oaspec@util@naming:to_snake_case(Prop_name@2)
),
{Prop_name@2, Prop_ref@1, Field_name@1}
end
),
_pipe@24 = gleam@list:filter(
_pipe@23,
fun(Entry@3) ->
{Prop_name@3, Prop_ref@2, _} = Entry@3,
gleam@option:is_none(
oaspec@codegen@schema_utils:constant_property_value(
Prop_ref@2,
Prop_name@3,
Required
)
)
end
),
gleam@list:map(
_pipe@24,
fun(Entry@4) ->
{_, _, Field_name@2} = Entry@4,
<<<<Field_name@2/binary, ": "/utf8>>/binary,
Field_name@2/binary>>
end
)
end,
Param_names@1 = case Additional_properties of
{typed, _} ->
lists:append(
Param_names,
[<<"additional_properties: additional_properties"/utf8>>]
);
untyped ->
lists:append(
Param_names,
[<<"additional_properties: additional_properties"/utf8>>]
);
forbidden ->
Param_names;
unspecified ->
Param_names
end,
Sb@6 = begin
_pipe@25 = Sb@5,
oaspec@util@string_extra:indent(
_pipe@25,
1,
<<<<<<<<"decode.success(types."/utf8, Type_name/binary>>/binary,
"("/utf8>>/binary,
(oaspec@util@string_extra:join_with(
Param_names@1,
<<", "/utf8>>
))/binary>>/binary,
"))"/utf8>>
)
end,
Sb@7 = begin
_pipe@26 = Sb@6,
_pipe@27 = oaspec@util@string_extra:line(_pipe@26, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@27)
end,
Sb@8 = begin
_pipe@28 = Sb@7,
_pipe@29 = oaspec@util@string_extra:line(
_pipe@28,
<<<<<<<<"pub fn "/utf8, Fn_name/binary>>/binary,
"(json_string: String) -> Result(types."/utf8>>/binary,
Type_name/binary>>/binary,
", json.DecodeError) {"/utf8>>
),
_pipe@30 = oaspec@util@string_extra:indent(
_pipe@29,
1,
<<<<"json.parse(json_string, "/utf8, Decoder_fn_name/binary>>/binary,
"())"/utf8>>
),
_pipe@31 = oaspec@util@string_extra:line(_pipe@30, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@31)
end,
List_fn_name = <<Fn_name/binary, "_list"/utf8>>,
List_decoder_fn_name = <<Decoder_fn_name/binary, "_list"/utf8>>,
_pipe@32 = Sb@8,
_pipe@33 = oaspec@util@string_extra:line(
_pipe@32,
<<<<<<<<"pub fn "/utf8, List_decoder_fn_name/binary>>/binary,
"() -> decode.Decoder(List(types."/utf8>>/binary,
Type_name/binary>>/binary,
")) {"/utf8>>
),
_pipe@34 = oaspec@util@string_extra:indent(
_pipe@33,
1,
<<<<"decode.list("/utf8, Decoder_fn_name/binary>>/binary, "())"/utf8>>
),
_pipe@35 = oaspec@util@string_extra:line(_pipe@34, <<"}"/utf8>>),
_pipe@36 = oaspec@util@string_extra:blank_line(_pipe@35),
_pipe@37 = oaspec@util@string_extra:line(
_pipe@36,
<<<<<<<<"pub fn "/utf8, List_fn_name/binary>>/binary,
"(json_string: String) -> Result(List(types."/utf8>>/binary,
Type_name/binary>>/binary,
"), json.DecodeError) {"/utf8>>
),
_pipe@38 = oaspec@util@string_extra:indent(
_pipe@37,
1,
<<<<"json.parse(json_string, "/utf8, List_decoder_fn_name/binary>>/binary,
"())"/utf8>>
),
_pipe@39 = oaspec@util@string_extra:line(_pipe@38, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@39).
-file("src/oaspec/codegen/decoders.gleam", 279).
?DOC(" Generate decoder functions for a schema.\n").
-spec generate_decoder(
gleam@string_tree:string_tree(),
binary(),
oaspec@openapi@schema:schema_ref(),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_decoder(Sb, Name, Schema_ref, Ctx) ->
Type_name = oaspec@util@naming:schema_to_type_name(Name),
Fn_name = <<"decode_"/utf8,
(oaspec@util@naming:to_snake_case(Name))/binary>>,
Decoder_fn_name = <<(oaspec@util@naming:to_snake_case(Name))/binary,
"_decoder"/utf8>>,
case Schema_ref of
{inline,
{object_schema,
Metadata,
Properties,
Required,
Additional_properties,
_,
_}} ->
generate_object_decoder(
Sb,
Name,
Type_name,
Fn_name,
Decoder_fn_name,
erlang:element(2, Metadata),
Properties,
Required,
Additional_properties,
Ctx
);
{inline, {string_schema, Metadata@1, _, Enum_values, _, _, _}} when Enum_values =/= [] ->
generate_enum_decoder(
Sb,
Type_name,
Fn_name,
Decoder_fn_name,
erlang:element(2, Metadata@1),
Enum_values
);
{inline, {all_of_schema, Metadata@2, Schemas}} ->
Merged = oaspec@codegen@types:merge_allof_schemas(Schemas, Ctx),
Merged_schema = {inline,
{object_schema,
Metadata@2,
erlang:element(2, Merged),
erlang:element(3, Merged),
erlang:element(4, Merged),
none,
none}},
generate_decoder(Sb, Name, Merged_schema, Ctx);
{inline, {one_of_schema, Metadata@3, Schemas@1, Discriminator}} ->
generate_oneof_decoder(
Sb,
Name,
Type_name,
Fn_name,
Decoder_fn_name,
erlang:element(2, Metadata@3),
Schemas@1,
Discriminator,
Ctx
);
{inline, {any_of_schema, Metadata@4, Schemas@2, _}} ->
generate_anyof_decoder(
Sb,
Type_name,
Fn_name,
Decoder_fn_name,
erlang:element(2, Metadata@4),
Schemas@2
);
{inline, {string_schema, Metadata@5, _, [], _, _, _}} ->
{Gleam_type, Decoder_expr} = case erlang:element(3, Metadata@5) of
true ->
{<<"Option(String)"/utf8>>,
<<"decode.optional(decode.string)"/utf8>>};
false ->
{<<"String"/utf8>>, <<"decode.string"/utf8>>}
end,
generate_primitive_decoder(
Sb,
Fn_name,
Decoder_fn_name,
Gleam_type,
Decoder_expr
);
{inline, {integer_schema, Metadata@6, _, _, _, _, _, _}} ->
{Gleam_type@1, Decoder_expr@1} = case erlang:element(3, Metadata@6) of
true ->
{<<"Option(Int)"/utf8>>,
<<"decode.optional(decode.int)"/utf8>>};
false ->
{<<"Int"/utf8>>, <<"decode.int"/utf8>>}
end,
generate_primitive_decoder(
Sb,
Fn_name,
Decoder_fn_name,
Gleam_type@1,
Decoder_expr@1
);
{inline, {number_schema, Metadata@7, _, _, _, _, _, _}} ->
{Gleam_type@2, Decoder_expr@2} = case erlang:element(3, Metadata@7) of
true ->
{<<"Option(Float)"/utf8>>,
<<"decode.optional(decode.float)"/utf8>>};
false ->
{<<"Float"/utf8>>, <<"decode.float"/utf8>>}
end,
generate_primitive_decoder(
Sb,
Fn_name,
Decoder_fn_name,
Gleam_type@2,
Decoder_expr@2
);
{inline, {boolean_schema, Metadata@8}} ->
{Gleam_type@3, Decoder_expr@3} = case erlang:element(3, Metadata@8) of
true ->
{<<"Option(Bool)"/utf8>>,
<<"decode.optional(decode.bool)"/utf8>>};
false ->
{<<"Bool"/utf8>>, <<"decode.bool"/utf8>>}
end,
generate_primitive_decoder(
Sb,
Fn_name,
Decoder_fn_name,
Gleam_type@3,
Decoder_expr@3
);
{inline, {array_schema, _, Items, _, _, _}} ->
generate_array_decoder(
Sb,
Name,
Fn_name,
Decoder_fn_name,
Items,
Ctx
);
_ ->
Sb
end.
-file("src/oaspec/codegen/decoders.gleam", 225).
?DOC(" Generate decoder for an anonymous schema with a composed name.\n").
-spec generate_anonymous_schema_decoder(
gleam@string_tree:string_tree(),
binary(),
binary(),
oaspec@openapi@schema:schema_object(),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_anonymous_schema_decoder(Sb, Op_id, Suffix, Schema_obj, Ctx) ->
Name = <<<<(oaspec@util@naming:to_snake_case(Op_id))/binary, "_"/utf8>>/binary,
(oaspec@util@naming:to_snake_case(Suffix))/binary>>,
Schema_ref = {inline, Schema_obj},
generate_decoder(Sb, Name, Schema_ref, Ctx).
-file("src/oaspec/codegen/decoders.gleam", 151).
?DOC(" Generate decoders for inline response schemas.\n").
-spec generate_anonymous_response_decoders(
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_response_decoders(Sb, Op_id, Operation, Ctx) ->
Responses = oaspec@util@http:sort_response_entries(
maps:to_list(erlang:element(8, Operation))
),
gleam@list:fold(
Responses,
Sb,
fun(Sb@1, Entry) ->
{Status_code, Ref_or_response} = Entry,
case Ref_or_response of
{value, Response} ->
Content_entries = oaspec@codegen@ir_build:sorted_entries(
erlang:element(3, Response)
),
case Content_entries of
[{_, Media_type} | _] ->
case erlang:element(2, Media_type) of
{some, {inline, Schema_obj}} ->
Filtered_schema = oaspec@codegen@types:filter_write_only_properties(
Schema_obj,
Ctx
),
Suffix = <<"Response"/utf8,
(oaspec@util@http:status_code_suffix(
Status_code
))/binary>>,
generate_anonymous_schema_decoder(
Sb@1,
Op_id,
Suffix,
Filtered_schema,
Ctx
);
_ ->
Sb@1
end;
_ ->
Sb@1
end;
{ref, _} ->
Sb@1
end
end
).
-file("src/oaspec/codegen/decoders.gleam", 190).
?DOC(" Generate decoder for an inline requestBody schema.\n").
-spec generate_anonymous_request_body_decoder(
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_decoder(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
),
generate_anonymous_schema_decoder(
Sb,
Op_id,
<<"RequestBody"/utf8>>,
Filtered_schema,
Ctx
);
_ ->
Sb
end;
_ ->
Sb
end;
{some, {ref, _}} ->
Sb;
none ->
Sb
end.
-file("src/oaspec/codegen/decoders.gleam", 138).
?DOC(" Generate decoders for anonymous inline schemas (response/requestBody).\n").
-spec generate_anonymous_decoders(
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_decoders(Sb, Ctx, Operations) ->
gleam@list:fold(
Operations,
Sb,
fun(Sb@1, Op) ->
{Op_id, Operation, _, _} = Op,
Sb@2 = generate_anonymous_response_decoders(
Sb@1,
Op_id,
Operation,
Ctx
),
generate_anonymous_request_body_decoder(Sb@2, Op_id, Operation, Ctx)
end
).
-file("src/oaspec/codegen/decoders.gleam", 238).
?DOC(" Generate inline enum decoders found in object/allOf properties.\n").
-spec generate_inline_enum_decoders(
gleam@string_tree:string_tree(),
binary(),
oaspec@openapi@schema:schema_ref(),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_inline_enum_decoders(Sb, Parent_name, Schema_ref, Ctx) ->
{Props, Required@1} = case Schema_ref of
{inline, {object_schema, _, Properties, Required, _, _, _}} ->
{oaspec@codegen@ir_build:sorted_entries(Properties), Required};
{inline, {all_of_schema, _, Schemas}} ->
Merged = oaspec@codegen@allof_merge:merge_allof_schemas(
Schemas,
Ctx
),
{oaspec@codegen@ir_build:sorted_entries(erlang:element(2, Merged)),
erlang:element(3, Merged)};
_ ->
{[], []}
end,
gleam@list:fold(
Props,
Sb,
fun(Sb@1, Entry) ->
{Prop_name, Prop_ref} = Entry,
case oaspec@codegen@schema_utils:constant_property_value(
Prop_ref,
Prop_name,
Required@1
) of
{some, _} ->
Sb@1;
none ->
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_decoder(Sb@1, Enum_name, Prop_ref, Ctx);
_ ->
Sb@1
end
end
end
).
-file("src/oaspec/codegen/decoders.gleam", 48).
?DOC(" Generate JSON decoders for all component schemas and anonymous types.\n").
-spec generate_decoders(
oaspec@codegen@context:context(),
list({binary(),
oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()),
binary(),
oaspec@openapi@spec:http_method()})
) -> binary().
generate_decoders(Ctx, Operations) ->
Schemas = case erlang:element(5, oaspec@codegen@context:spec(Ctx)) of
{some, Components} ->
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
);
none ->
[]
end,
Needs_option = gleam@list:any(
Schemas,
fun(Entry) ->
{_, Schema_ref} = Entry,
oaspec@codegen@types:schema_has_optional_fields(Schema_ref, Ctx)
end
),
Needs_dict = gleam@list:any(
Schemas,
fun(Entry@1) ->
{_, Schema_ref@1} = Entry@1,
oaspec@codegen@types:schema_has_additional_properties(
Schema_ref@1,
Ctx
)
end
),
Needs_types = gleam@list:any(
Schemas,
fun(Entry@2) ->
{_, Schema_ref@2} = Entry@2,
case Schema_ref@2 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
),
Base_imports = case Needs_dict of
true ->
[<<"gleam/dict"/utf8>>,
<<"gleam/dynamic/decode"/utf8>>,
<<"gleam/json"/utf8>>];
false ->
[<<"gleam/dynamic/decode"/utf8>>, <<"gleam/json"/utf8>>]
end,
Base_imports@1 = case Needs_types of
true ->
lists:append(
Base_imports,
[<<(oaspec@config:package(oaspec@codegen@context:config(Ctx)))/binary,
"/types"/utf8>>]
);
false ->
Base_imports
end,
Imports = case Needs_option of
true ->
lists:append(Base_imports@1, [<<"gleam/option"/utf8>>]);
false ->
Base_imports@1
end,
Sb = begin
_pipe = oaspec@util@string_extra:file_header(<<"0.25.0"/utf8>>),
oaspec@util@string_extra:imports(_pipe, Imports)
end,
Schemas@1 = case erlang:element(5, oaspec@codegen@context:spec(Ctx)) of
{some, Components@1} ->
_pipe@1 = gleam@list:sort(
maps:to_list(erlang:element(2, Components@1)),
fun(A@1, B@1) ->
gleam@string:compare(
erlang:element(1, A@1),
erlang:element(1, B@1)
)
end
),
gleam@list:filter(
_pipe@1,
fun(Entry@3) ->
not oaspec@codegen@ir_build:is_internal_schema(
erlang:element(2, Entry@3)
)
end
);
none ->
[]
end,
Sb@2 = gleam@list:fold(
Schemas@1,
Sb,
fun(Sb@1, Entry@4) ->
{Name, Schema_ref@3} = Entry@4,
generate_inline_enum_decoders(Sb@1, Name, Schema_ref@3, Ctx)
end
),
Sb@4 = gleam@list:fold(
Schemas@1,
Sb@2,
fun(Sb@3, Entry@5) ->
{Name@1, Schema_ref@4} = Entry@5,
generate_decoder(Sb@3, Name@1, Schema_ref@4, Ctx)
end
),
Sb@5 = generate_anonymous_decoders(Sb@4, Ctx, Operations),
oaspec@util@string_extra:to_string(Sb@5).
-file("src/oaspec/codegen/decoders.gleam", 29).
?DOC(
" Generate the `decode.gleam` module for the resolved spec.\n"
"\n"
" Encoder generation lives in `src/oaspec/codegen/encoders.gleam`; see\n"
" `generate.gleam::generate_shared` for how the two halves are combined.\n"
).
-spec generate(oaspec@codegen@context:context()) -> list(oaspec@codegen@context:generated_file()).
generate(Ctx) ->
Operations = oaspec@openapi@operations:collect_operations(Ctx),
Decode_content = generate_decoders(Ctx, Operations),
[{generated_file,
<<"decode.gleam"/utf8>>,
Decode_content,
shared_target,
overwrite}].