Current section

Files

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

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", 241).
?DOC(" Convert a SchemaRef to a decoder expression string.\n").
-spec schema_ref_to_decoder(
oaspec@openapi@schema:schema_ref(),
oaspec@codegen@context:context()
) -> binary().
schema_ref_to_decoder(Ref, Ctx) ->
_ = Ctx,
case Ref of
{inline, {string_schema, _, _, _, _, _, _, _}} ->
<<"decode.string"/utf8>>;
{inline, {integer_schema, _, _, _, _, _}} ->
<<"decode.int"/utf8>>;
{inline, {number_schema, _, _, _, _, _}} ->
<<"decode.float"/utf8>>;
{inline, {boolean_schema, _, _}} ->
<<"decode.bool"/utf8>>;
{inline, {array_schema, _, Items, _, _, _}} ->
Inner = schema_ref_to_decoder(Items, Ctx),
<<<<"decode.list("/utf8, Inner/binary>>/binary, ")"/utf8>>;
{reference, Ref@1} ->
Name = oaspec@openapi@resolver:ref_to_name(Ref@1),
<<(oaspec@util@naming:to_snake_case(Name))/binary,
"_decoder()"/utf8>>;
_ ->
<<"decode.string"/utf8>>
end.
-file("src/oaspec/codegen/decoders.gleam", 261).
?DOC(" Check if a SchemaRef has nullable: true.\n").
-spec schema_ref_is_nullable(oaspec@openapi@schema:schema_ref()) -> boolean().
schema_ref_is_nullable(Ref) ->
case Ref of
{inline, Schema} ->
oaspec@openapi@schema:is_nullable(Schema);
{reference, _} ->
false
end.
-file("src/oaspec/codegen/decoders.gleam", 457).
?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(),
oaspec@codegen@context:context()
) -> binary().
schema_ref_to_json_encoder_fn(Ref, Ctx) ->
_ = Ctx,
case Ref of
{inline, {string_schema, _, _, _, _, _, _, _}} ->
<<"json.string"/utf8>>;
{inline, {integer_schema, _, _, _, _, _}} ->
<<"json.int"/utf8>>;
{inline, {number_schema, _, _, _, _, _}} ->
<<"json.float"/utf8>>;
{inline, {boolean_schema, _, _}} ->
<<"json.bool"/utf8>>;
{reference, Ref@1} ->
Name = oaspec@openapi@resolver:ref_to_name(Ref@1),
<<<<"encode_"/utf8,
(oaspec@util@naming:to_snake_case(Name))/binary>>/binary,
"_json"/utf8>>;
_ ->
<<"json.string"/utf8>>
end.
-file("src/oaspec/codegen/decoders.gleam", 433).
?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(),
oaspec@codegen@context:context()
) -> binary().
schema_ref_to_json_encoder(Value_expr, Ref, Ctx) ->
case Ref of
{inline, {string_schema, _, _, _, _, _, _, _}} ->
<<<<"json.string("/utf8, Value_expr/binary>>/binary, ")"/utf8>>;
{inline, {integer_schema, _, _, _, _, _}} ->
<<<<"json.int("/utf8, Value_expr/binary>>/binary, ")"/utf8>>;
{inline, {number_schema, _, _, _, _, _}} ->
<<<<"json.float("/utf8, Value_expr/binary>>/binary, ")"/utf8>>;
{inline, {boolean_schema, _, _}} ->
<<<<"json.bool("/utf8, Value_expr/binary>>/binary, ")"/utf8>>;
{inline, {array_schema, _, Items, _, _, _}} ->
Inner_fn = schema_ref_to_json_encoder_fn(Items, Ctx),
<<<<<<<<"json.array("/utf8, Value_expr/binary>>/binary, ", "/utf8>>/binary,
Inner_fn/binary>>/binary,
")"/utf8>>;
{reference, Ref@1} ->
Name = oaspec@openapi@resolver:ref_to_name(Ref@1),
<<<<<<<<"encode_"/utf8,
(oaspec@util@naming:to_snake_case(Name))/binary>>/binary,
"_json("/utf8>>/binary,
Value_expr/binary>>/binary,
")"/utf8>>;
_ ->
<<<<"json.string("/utf8, Value_expr/binary>>/binary, ")"/utf8>>
end.
-file("src/oaspec/codegen/decoders.gleam", 299).
?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, _, _, _}} ->
Sb@1 = begin
_pipe = Sb,
_pipe@1 = 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>>
),
oaspec@util@string_extra:indent(
_pipe@1,
1,
<<"json.object(["/utf8>>
)
end,
Props = maps:to_list(Properties),
Sb@3 = gleam@list:index_fold(
Props,
Sb@1,
fun(Sb@2, Entry, Idx) ->
{Prop_name, Prop_ref} = Entry,
Field_name = oaspec@util@naming:to_snake_case(Prop_name),
Is_required = gleam@list:contains(Required, Prop_name),
Trailing = case Idx =:= (erlang:length(Props) - 1) of
true ->
<<""/utf8>>;
false ->
<<","/utf8>>
end,
Encoder_expr = schema_ref_to_json_encoder(
<<"value."/utf8, Field_name/binary>>,
Prop_ref,
Ctx
),
case Is_required of
true ->
_pipe@2 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@2,
2,
<<<<<<<<<<"#(\""/utf8, Prop_name/binary>>/binary,
"\", "/utf8>>/binary,
Encoder_expr/binary>>/binary,
")"/utf8>>/binary,
Trailing/binary>>
);
false ->
_pipe@3 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@3,
2,
<<<<<<<<<<<<<<"#(\""/utf8, Prop_name/binary>>/binary,
"\", json.nullable(value."/utf8>>/binary,
Field_name/binary>>/binary,
", "/utf8>>/binary,
(schema_ref_to_json_encoder_fn(
Prop_ref,
Ctx
))/binary>>/binary,
"))"/utf8>>/binary,
Trailing/binary>>
)
end
end
),
Sb@4 = begin
_pipe@4 = Sb@3,
_pipe@5 = oaspec@util@string_extra:indent(
_pipe@4,
1,
<<"])"/utf8>>
),
_pipe@6 = oaspec@util@string_extra:line(_pipe@5, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@6)
end,
Sb@5 = begin
_pipe@7 = Sb@4,
_pipe@8 = oaspec@util@string_extra:line(
_pipe@7,
<<<<<<<<"pub fn "/utf8, Fn_name/binary>>/binary,
"(value: types."/utf8>>/binary,
Type_name/binary>>/binary,
") -> String {"/utf8>>
),
_pipe@9 = oaspec@util@string_extra:indent(
_pipe@8,
1,
<<Json_fn_name/binary, "(value) |> json.to_string()"/utf8>>
),
_pipe@10 = oaspec@util@string_extra:line(_pipe@9, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@10)
end,
Sb@5;
{inline, {string_schema, _, _, Enum_values, _, _, _, _}} when Enum_values =/= [] ->
Sb@6 = begin
_pipe@11 = Sb,
_pipe@12 = oaspec@util@string_extra:line(
_pipe@11,
<<<<<<<<"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@12,
1,
<<"let str = case value {"/utf8>>
)
end,
Sb@8 = gleam@list:fold(
Enum_values,
Sb@6,
fun(Sb@7, Value) ->
Variant = oaspec@util@naming:schema_to_type_name(
<<<<Type_name/binary, "_"/utf8>>/binary, Value/binary>>
),
_pipe@13 = Sb@7,
oaspec@util@string_extra:indent(
_pipe@13,
2,
<<<<<<<<"types."/utf8, Variant/binary>>/binary,
" -> \""/utf8>>/binary,
Value/binary>>/binary,
"\""/utf8>>
)
end
),
Sb@9 = begin
_pipe@14 = Sb@8,
_pipe@15 = oaspec@util@string_extra:indent(
_pipe@14,
1,
<<"}"/utf8>>
),
_pipe@16 = oaspec@util@string_extra:indent(
_pipe@15,
1,
<<"json.string(str)"/utf8>>
),
_pipe@17 = oaspec@util@string_extra:line(_pipe@16, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@17)
end,
Sb@10 = begin
_pipe@18 = Sb@9,
_pipe@19 = oaspec@util@string_extra:line(
_pipe@18,
<<<<<<<<"pub fn "/utf8, Fn_name/binary>>/binary,
"(value: types."/utf8>>/binary,
Type_name/binary>>/binary,
") -> String {"/utf8>>
),
_pipe@20 = oaspec@util@string_extra:indent(
_pipe@19,
1,
<<Json_fn_name/binary, "(value) |> json.to_string()"/utf8>>
),
_pipe@21 = oaspec@util@string_extra:line(_pipe@20, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@21)
end,
Sb@10;
_ ->
Sb
end.
-file("src/oaspec/codegen/decoders.gleam", 273).
?DOC(" Generate JSON encoders for all component schemas.\n").
-spec generate_encoders(oaspec@codegen@context:context()) -> binary().
generate_encoders(Ctx) ->
Sb = begin
_pipe = oaspec@util@string_extra:file_header(<<"0.1.2"/utf8>>),
oaspec@util@string_extra:imports(
_pipe,
[<<"gleam/json"/utf8>>,
<<(erlang:element(5, erlang:element(3, Ctx)))/binary,
"/types"/utf8>>]
)
end,
Schemas = case erlang:element(5, erlang:element(2, Ctx)) of
{some, Components} ->
maps:to_list(erlang:element(2, Components));
none ->
[]
end,
Sb@2 = gleam@list:fold(
Schemas,
Sb,
fun(Sb@1, Entry) ->
{Name, Schema_ref} = Entry,
generate_encoder(Sb@1, Name, Schema_ref, Ctx)
end
),
oaspec@util@string_extra:to_string(Sb@2).
-file("src/oaspec/codegen/decoders.gleam", 473).
?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", 54).
?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, Description, Properties, Required, _, _, Nullable}} ->
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 = maps:to_list(Properties),
Sb@4 = gleam@list:fold(
Props,
Sb@2,
fun(Sb@3, Entry) ->
{Prop_name, Prop_ref} = Entry,
Field_name = oaspec@util@naming:to_snake_case(Prop_name),
Is_required = gleam@list:contains(Required, Prop_name),
Field_decoder = schema_ref_to_decoder(Prop_ref, Ctx),
Is_nullable_schema = schema_ref_is_nullable(Prop_ref),
case Is_required of
true ->
_pipe@1 = Sb@3,
oaspec@util@string_extra:indent(
_pipe@1,
1,
<<<<<<<<<<<<"use "/utf8, Field_name/binary>>/binary,
" <- decode.field(\""/utf8>>/binary,
Prop_name/binary>>/binary,
"\", "/utf8>>/binary,
Field_decoder/binary>>/binary,
")"/utf8>>
);
false ->
case Is_nullable_schema of
true ->
_pipe@2 = Sb@3,
oaspec@util@string_extra:indent(
_pipe@2,
1,
<<<<<<<<<<<<"use "/utf8,
Field_name/binary>>/binary,
" <- decode.optional_field(\""/utf8>>/binary,
Prop_name/binary>>/binary,
"\", option.None, "/utf8>>/binary,
Field_decoder/binary>>/binary,
")"/utf8>>
);
false ->
_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, decode.optional("/utf8>>/binary,
Field_decoder/binary>>/binary,
"))"/utf8>>
)
end
end
end
),
Param_names = gleam@list:map(
Props,
fun(Entry@1) ->
{Prop_name@1, _} = Entry@1,
Field_name@1 = oaspec@util@naming:to_snake_case(Prop_name@1),
<<<<Field_name@1/binary, ": "/utf8>>/binary,
Field_name@1/binary>>
end
),
Sb@5 = begin
_pipe@4 = Sb@4,
oaspec@util@string_extra:indent(
_pipe@4,
1,
<<<<<<<<"decode.success(types."/utf8, Type_name/binary>>/binary,
"("/utf8>>/binary,
(oaspec@util@string_extra:join_with(
Param_names,
<<", "/utf8>>
))/binary>>/binary,
"))"/utf8>>
)
end,
Sb@6 = begin
_pipe@5 = Sb@5,
_pipe@6 = oaspec@util@string_extra:line(_pipe@5, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@6)
end,
Sb@7 = begin
_pipe@7 = Sb@6,
_pipe@8 = oaspec@util@string_extra:line(
_pipe@7,
<<<<<<<<"pub fn "/utf8, Fn_name/binary>>/binary,
"(json_string: String) -> Result(types."/utf8>>/binary,
Type_name/binary>>/binary,
", json.DecodeError) {"/utf8>>
),
_pipe@9 = oaspec@util@string_extra:indent(
_pipe@8,
1,
<<<<"json.parse(json_string, "/utf8,
Decoder_fn_name/binary>>/binary,
"())"/utf8>>
),
_pipe@10 = oaspec@util@string_extra:line(_pipe@9, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@10)
end,
_ = Nullable,
Sb@7;
{inline, {string_schema, Description@1, _, Enum_values, _, _, _, _}} when Enum_values =/= [] ->
Sb@8 = maybe_doc_comment(Sb, Description@1),
Sb@9 = begin
_pipe@11 = Sb@8,
_pipe@12 = oaspec@util@string_extra:line(
_pipe@11,
<<<<<<<<"pub fn "/utf8, Decoder_fn_name/binary>>/binary,
"() -> decode.Decoder(types."/utf8>>/binary,
Type_name/binary>>/binary,
") {"/utf8>>
),
_pipe@13 = oaspec@util@string_extra:indent(
_pipe@12,
1,
<<"use value <- decode.then(decode.string)"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@13,
1,
<<"case value {"/utf8>>
)
end,
Sb@11 = gleam@list:fold(
Enum_values,
Sb@9,
fun(Sb@10, Value) ->
Variant = oaspec@util@naming:schema_to_type_name(
<<<<Type_name/binary, "_"/utf8>>/binary, Value/binary>>
),
_pipe@14 = Sb@10,
oaspec@util@string_extra:indent(
_pipe@14,
2,
<<<<<<<<"\""/utf8, Value/binary>>/binary,
"\" -> decode.success(types."/utf8>>/binary,
Variant/binary>>/binary,
")"/utf8>>
)
end
),
Sb@12 = begin
_pipe@15 = Sb@11,
_pipe@16 = oaspec@util@string_extra:indent(
_pipe@15,
2,
<<<<<<<<"_ -> decode.failure(types."/utf8,
(oaspec@util@naming:schema_to_type_name(
<<<<Type_name/binary, "_"/utf8>>/binary,
((case Enum_values of
[First | _] ->
First;
[] ->
<<"unknown"/utf8>>
end))/binary>>
))/binary>>/binary,
", \""/utf8>>/binary,
Type_name/binary>>/binary,
"\")"/utf8>>
),
_pipe@17 = oaspec@util@string_extra:indent(
_pipe@16,
1,
<<"}"/utf8>>
),
_pipe@18 = oaspec@util@string_extra:line(_pipe@17, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@18)
end,
Sb@13 = begin
_pipe@19 = Sb@12,
_pipe@20 = oaspec@util@string_extra:line(
_pipe@19,
<<<<<<<<"pub fn "/utf8, Fn_name/binary>>/binary,
"(json_string: String) -> Result(types."/utf8>>/binary,
Type_name/binary>>/binary,
", json.DecodeError) {"/utf8>>
),
_pipe@21 = oaspec@util@string_extra:indent(
_pipe@20,
1,
<<<<"json.parse(json_string, "/utf8,
Decoder_fn_name/binary>>/binary,
"())"/utf8>>
),
_pipe@22 = oaspec@util@string_extra:line(_pipe@21, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@22)
end,
Sb@13;
_ ->
Sb
end.
-file("src/oaspec/codegen/decoders.gleam", 29).
?DOC(" Generate JSON decoders for all component schemas.\n").
-spec generate_decoders(oaspec@codegen@context:context()) -> binary().
generate_decoders(Ctx) ->
Sb = begin
_pipe = oaspec@util@string_extra:file_header(<<"0.1.2"/utf8>>),
oaspec@util@string_extra:imports(
_pipe,
[<<"gleam/dynamic/decode"/utf8>>,
<<"gleam/json"/utf8>>,
<<"gleam/option"/utf8>>,
<<(erlang:element(5, erlang:element(3, Ctx)))/binary,
"/types"/utf8>>]
)
end,
Schemas = case erlang:element(5, erlang:element(2, Ctx)) of
{some, Components} ->
maps:to_list(erlang:element(2, Components));
none ->
[]
end,
Sb@2 = gleam@list:fold(
Schemas,
Sb,
fun(Sb@1, Entry) ->
{Name, Schema_ref} = Entry,
generate_decoder(Sb@1, Name, Schema_ref, Ctx)
end
),
oaspec@util@string_extra:to_string(Sb@2).
-file("src/oaspec/codegen/decoders.gleam", 14).
?DOC(" Generate decoder and encoder modules.\n").
-spec generate(oaspec@codegen@context:context()) -> list(oaspec@codegen@context:generated_file()).
generate(Ctx) ->
Decode_content = generate_decoders(Ctx),
Encode_content = generate_encoders(Ctx),
[{generated_file, <<"decode.gleam"/utf8>>, Decode_content},
{generated_file, <<"encode.gleam"/utf8>>, Encode_content}].