Current section

Files

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

src/oaspec@codegen@types.erl

-module(oaspec@codegen@types).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/oaspec/codegen/types.gleam").
-export([schema_to_gleam_type/2, schema_ref_to_type/2, collect_operations/1, schema_has_optional_fields/2, 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/types.gleam", 481).
?DOC(" Get a status code suffix for anonymous type names.\n").
-spec status_code_suffix(binary()) -> binary().
status_code_suffix(Code) ->
case Code of
<<"200"/utf8>> ->
<<"Ok"/utf8>>;
<<"201"/utf8>> ->
<<"Created"/utf8>>;
<<"204"/utf8>> ->
<<"NoContent"/utf8>>;
<<"400"/utf8>> ->
<<"BadRequest"/utf8>>;
<<"401"/utf8>> ->
<<"Unauthorized"/utf8>>;
<<"403"/utf8>> ->
<<"Forbidden"/utf8>>;
<<"404"/utf8>> ->
<<"NotFound"/utf8>>;
<<"409"/utf8>> ->
<<"Conflict"/utf8>>;
<<"422"/utf8>> ->
<<"UnprocessableEntity"/utf8>>;
<<"500"/utf8>> ->
<<"InternalServerError"/utf8>>;
<<"default"/utf8>> ->
<<"Default"/utf8>>;
Other ->
<<"Status"/utf8, Other/binary>>
end.
-file("src/oaspec/codegen/types.gleam", 589).
?DOC(" Convert a schema object to a Gleam type string.\n").
-spec schema_to_gleam_type(
oaspec@openapi@schema:schema_object(),
oaspec@codegen@context:context()
) -> binary().
schema_to_gleam_type(Schema, _) ->
Base_type = case Schema of
{string_schema, _, _, _, _, _, _, _} ->
<<"String"/utf8>>;
{integer_schema, _, _, _, _, _} ->
<<"Int"/utf8>>;
{number_schema, _, _, _, _, _} ->
<<"Float"/utf8>>;
{boolean_schema, _, _} ->
<<"Bool"/utf8>>;
{array_schema, _, Items, _, _, _} ->
case Items of
{reference, Ref} ->
Name = oaspec@openapi@resolver:ref_to_name(Ref),
<<<<"List("/utf8,
(oaspec@util@naming:schema_to_type_name(Name))/binary>>/binary,
")"/utf8>>;
{inline, Inner} ->
Inner_type = case Inner of
{string_schema, _, _, _, _, _, _, _} ->
<<"String"/utf8>>;
{integer_schema, _, _, _, _, _} ->
<<"Int"/utf8>>;
{number_schema, _, _, _, _, _} ->
<<"Float"/utf8>>;
{boolean_schema, _, _} ->
<<"Bool"/utf8>>;
_ ->
<<"String"/utf8>>
end,
<<<<"List("/utf8, Inner_type/binary>>/binary, ")"/utf8>>
end;
{object_schema, _, _, _, _, _, _} ->
<<"String"/utf8>>;
{all_of_schema, _, _} ->
<<"String"/utf8>>;
{one_of_schema, _, _, _} ->
<<"String"/utf8>>;
{any_of_schema, _, _} ->
<<"String"/utf8>>
end,
case oaspec@openapi@schema:is_nullable(Schema) of
true ->
<<<<"Option("/utf8, Base_type/binary>>/binary, ")"/utf8>>;
false ->
Base_type
end.
-file("src/oaspec/codegen/types.gleam", 517).
?DOC(" Convert a schema to a qualified Gleam type with types. prefix for compound types.\n").
-spec schema_to_gleam_type_qualified(
oaspec@openapi@schema:schema_object(),
binary(),
binary(),
oaspec@codegen@context:context()
) -> binary().
schema_to_gleam_type_qualified(Schema_obj, Op_id, Suffix, Ctx) ->
case Schema_obj of
{array_schema, _, Items, _, _, _} ->
case Items of
{reference, Ref} ->
Name = oaspec@openapi@resolver:ref_to_name(Ref),
<<<<"List(types."/utf8,
(oaspec@util@naming:schema_to_type_name(Name))/binary>>/binary,
")"/utf8>>;
_ ->
schema_to_gleam_type(Schema_obj, Ctx)
end;
{object_schema, _, _, _, _, _, _} ->
Type_name = <<(oaspec@util@naming:schema_to_type_name(Op_id))/binary,
Suffix/binary>>,
<<"types."/utf8, Type_name/binary>>;
{one_of_schema, _, Schemas, _} ->
All_refs = gleam@list:all(Schemas, fun(S) -> case S of
{reference, _} ->
true;
_ ->
false
end end),
case All_refs of
true ->
Type_name@1 = <<(oaspec@util@naming:schema_to_type_name(
Op_id
))/binary,
Suffix/binary>>,
<<"types."/utf8, Type_name@1/binary>>;
false ->
schema_to_gleam_type(Schema_obj, Ctx)
end;
{any_of_schema, _, Schemas@1} ->
All_refs@1 = gleam@list:all(Schemas@1, fun(S@1) -> case S@1 of
{reference, _} ->
true;
_ ->
false
end end),
case All_refs@1 of
true ->
Type_name@2 = <<(oaspec@util@naming:schema_to_type_name(
Op_id
))/binary,
Suffix/binary>>,
<<"types."/utf8, Type_name@2/binary>>;
false ->
schema_to_gleam_type(Schema_obj, Ctx)
end;
{all_of_schema, _, _} ->
Type_name@3 = <<(oaspec@util@naming:schema_to_type_name(Op_id))/binary,
Suffix/binary>>,
<<"types."/utf8, Type_name@3/binary>>;
_ ->
schema_to_gleam_type(Schema_obj, Ctx)
end.
-file("src/oaspec/codegen/types.gleam", 500).
?DOC(
" Convert a SchemaRef to a qualified Gleam type string (with types. prefix).\n"
" Used in response_types and request_types where types are in a separate module.\n"
).
-spec schema_ref_to_type_qualified(
oaspec@openapi@schema:schema_ref(),
binary(),
binary(),
oaspec@codegen@context:context()
) -> binary().
schema_ref_to_type_qualified(Ref, Op_id, Suffix, Ctx) ->
case Ref of
{inline, Schema_obj} ->
schema_to_gleam_type_qualified(Schema_obj, Op_id, Suffix, Ctx);
{reference, Ref@1} ->
Name = oaspec@openapi@resolver:ref_to_name(Ref@1),
<<"types."/utf8,
(oaspec@util@naming:schema_to_type_name(Name))/binary>>
end.
-file("src/oaspec/codegen/types.gleam", 578).
?DOC(" Convert a SchemaRef to a Gleam type string.\n").
-spec schema_ref_to_type(
oaspec@openapi@schema:schema_ref(),
oaspec@codegen@context:context()
) -> binary().
schema_ref_to_type(Ref, Ctx) ->
case Ref of
{inline, Schema} ->
schema_to_gleam_type(Schema, Ctx);
{reference, Ref@1} ->
Name = oaspec@openapi@resolver:ref_to_name(Ref@1),
oaspec@util@naming:schema_to_type_name(Name)
end.
-file("src/oaspec/codegen/types.gleam", 288).
?DOC(" Convert a SchemaRef to a type string, using inline enum type if applicable.\n").
-spec schema_ref_to_type_with_inline_enum(
oaspec@openapi@schema:schema_ref(),
binary(),
binary(),
oaspec@codegen@context:context()
) -> binary().
schema_ref_to_type_with_inline_enum(Ref, Parent_name, Prop_name, Ctx) ->
case Ref of
{inline, {string_schema, _, _, Enum_values, _, _, _, _}} when Enum_values =/= [] ->
<<(oaspec@util@naming:schema_to_type_name(Parent_name))/binary,
(oaspec@util@naming:schema_to_type_name(Prop_name))/binary>>;
_ ->
schema_ref_to_type(Ref, Ctx)
end.
-file("src/oaspec/codegen/types.gleam", 808).
?DOC(
" Convert an HTTP status code to a Gleam variant name.\n"
" Prefixed with the type name to avoid duplicate constructors across types.\n"
).
-spec status_code_to_variant(binary(), binary()) -> binary().
status_code_to_variant(Code, Type_name) ->
Suffix = case Code of
<<"200"/utf8>> ->
<<"Ok"/utf8>>;
<<"201"/utf8>> ->
<<"Created"/utf8>>;
<<"204"/utf8>> ->
<<"NoContent"/utf8>>;
<<"400"/utf8>> ->
<<"BadRequest"/utf8>>;
<<"401"/utf8>> ->
<<"Unauthorized"/utf8>>;
<<"403"/utf8>> ->
<<"Forbidden"/utf8>>;
<<"404"/utf8>> ->
<<"NotFound"/utf8>>;
<<"409"/utf8>> ->
<<"Conflict"/utf8>>;
<<"422"/utf8>> ->
<<"UnprocessableEntity"/utf8>>;
<<"500"/utf8>> ->
<<"InternalServerError"/utf8>>;
<<"default"/utf8>> ->
<<"Default"/utf8>>;
Other ->
<<"Status"/utf8, Other/binary>>
end,
<<Type_name/binary, Suffix/binary>>.
-file("src/oaspec/codegen/types.gleam", 763).
?DOC(" Generate a response type for an operation.\n").
-spec generate_response_type(
gleam@string_tree:string_tree(),
binary(),
oaspec@openapi@spec:operation(),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_response_type(Sb, Op_id, Operation, Ctx) ->
Type_name = <<(oaspec@util@naming:schema_to_type_name(Op_id))/binary,
"Response"/utf8>>,
Responses = maps:to_list(erlang:element(8, Operation)),
case gleam@list:is_empty(Responses) of
true ->
Sb;
false ->
Sb@1 = begin
_pipe = Sb,
oaspec@util@string_extra:line(
_pipe,
<<<<"pub type "/utf8, Type_name/binary>>/binary, " {"/utf8>>
)
end,
Sb@3 = gleam@list:fold(
Responses,
Sb@1,
fun(Sb@2, Entry) ->
{Status_code, Response} = Entry,
Variant_name = status_code_to_variant(
Status_code,
Type_name
),
Content_entries = maps:to_list(erlang:element(3, Response)),
case Content_entries of
[] ->
_pipe@1 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@1,
1,
Variant_name
);
[{_, Media_type} | _] ->
case erlang:element(2, Media_type) of
{some, Ref} ->
Suffix = <<"Response"/utf8,
(status_code_suffix(Status_code))/binary>>,
Inner_type = schema_ref_to_type_qualified(
Ref,
Op_id,
Suffix,
Ctx
),
_pipe@2 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@2,
1,
<<<<<<Variant_name/binary, "("/utf8>>/binary,
Inner_type/binary>>/binary,
")"/utf8>>
);
none ->
_pipe@3 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@3,
1,
Variant_name
)
end
end
end
),
_pipe@4 = Sb@3,
_pipe@5 = oaspec@util@string_extra:line(_pipe@4, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@5)
end.
-file("src/oaspec/codegen/types.gleam", 827).
?DOC(" Collect all operations from the spec with their IDs, paths, and methods.\n").
-spec collect_operations(oaspec@codegen@context:context()) -> list({binary(),
oaspec@openapi@spec:operation(),
binary(),
oaspec@openapi@spec:http_method()}).
collect_operations(Ctx) ->
Paths = gleam@list:sort(
maps:to_list(erlang:element(4, erlang:element(2, Ctx))),
fun(A, B) ->
gleam@string:compare(erlang:element(1, A), erlang:element(1, B))
end
),
gleam@list:flat_map(
Paths,
fun(Entry) ->
{Path, Path_item} = Entry,
Ops = [{erlang:element(4, Path_item), get},
{erlang:element(5, Path_item), post},
{erlang:element(6, Path_item), put},
{erlang:element(7, Path_item), delete},
{erlang:element(8, Path_item), patch}],
gleam@list:filter_map(
Ops,
fun(Op_entry) ->
{Maybe_op, Method} = Op_entry,
case Maybe_op of
{some, Operation} ->
Op_param_keys = gleam@list:map(
erlang:element(6, Operation),
fun(P) ->
{erlang:element(2, P), erlang:element(3, P)}
end
),
Inherited_params = gleam@list:filter(
erlang:element(9, Path_item),
fun(P@1) ->
not gleam@list:contains(
Op_param_keys,
{erlang:element(2, P@1),
erlang:element(3, P@1)}
)
end
),
Merged_params = lists:append(
Inherited_params,
erlang:element(6, Operation)
),
Effective_security = case erlang:element(
10,
Operation
) of
{some, Sec} ->
Sec;
none ->
erlang:element(7, erlang:element(2, Ctx))
end,
Operation@1 = {operation,
erlang:element(2, Operation),
erlang:element(3, Operation),
erlang:element(4, Operation),
erlang:element(5, Operation),
Merged_params,
erlang:element(7, Operation),
erlang:element(8, Operation),
erlang:element(9, Operation),
{some, Effective_security}},
Op_id = case erlang:element(2, Operation@1) of
{some, Id} ->
Id;
none ->
<<<<(oaspec@openapi@spec:method_to_lower(
Method
))/binary,
"_"/utf8>>/binary,
(begin
_pipe = gleam@string:replace(
Path,
<<"/"/utf8>>,
<<"_"/utf8>>
),
_pipe@1 = gleam@string:replace(
_pipe,
<<"{"/utf8>>,
<<""/utf8>>
),
gleam@string:replace(
_pipe@1,
<<"}"/utf8>>,
<<""/utf8>>
)
end)/binary>>
end,
{ok, {Op_id, Operation@1, Path, Method}};
none ->
{error, nil}
end
end
)
end
).
-file("src/oaspec/codegen/types.gleam", 746).
?DOC(" Generate response types for all operations.\n").
-spec generate_response_types(oaspec@codegen@context:context()) -> binary().
generate_response_types(Ctx) ->
Sb = begin
_pipe = oaspec@util@string_extra:file_header(<<"0.3.0"/utf8>>),
oaspec@util@string_extra:imports(
_pipe,
[<<(erlang:element(5, erlang:element(3, Ctx)))/binary,
"/types"/utf8>>]
)
end,
Operations = collect_operations(Ctx),
Sb@2 = gleam@list:fold(
Operations,
Sb,
fun(Sb@1, Op) ->
{Op_id, Operation, _, _} = Op,
generate_response_type(Sb@1, Op_id, Operation, Ctx)
end
),
oaspec@util@string_extra:to_string(Sb@2).
-file("src/oaspec/codegen/types.gleam", 913).
?DOC(" Check if a SchemaRef is nullable, resolving $ref if needed.\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, S} ->
oaspec@openapi@schema:is_nullable(S);
{reference, _} ->
case oaspec@openapi@resolver:resolve_schema_ref(
Ref,
erlang:element(2, Ctx)
) of
{ok, S@1} ->
oaspec@openapi@schema:is_nullable(S@1);
{error, _} ->
false
end
end.
-file("src/oaspec/codegen/types.gleam", 889).
?DOC(" Check if a schema has any optional or nullable fields that would need Option.\n").
-spec schema_has_optional_fields(
oaspec@openapi@schema:schema_ref(),
oaspec@codegen@context:context()
) -> boolean().
schema_has_optional_fields(Schema_ref, Ctx) ->
case Schema_ref of
{inline, {object_schema, _, Properties, Required, _, _, _}} ->
Has_optional = begin
_pipe = maps:to_list(Properties),
gleam@list:any(
_pipe,
fun(Entry) ->
{Prop_name, Prop_ref} = Entry,
not gleam@list:contains(Required, Prop_name) orelse schema_ref_is_nullable(
Prop_ref,
Ctx
)
end
)
end,
Has_optional;
{inline, {all_of_schema, _, Schemas}} ->
gleam@list:any(
Schemas,
fun(S) -> schema_has_optional_fields(S, Ctx) end
);
{reference, _} ->
case oaspec@openapi@resolver:resolve_schema_ref(
Schema_ref,
erlang:element(2, Ctx)
) of
{ok, Schema_obj} ->
schema_has_optional_fields({inline, Schema_obj}, Ctx);
{error, _} ->
false
end;
_ ->
false
end.
-file("src/oaspec/codegen/types.gleam", 946).
?DOC(" Extract the type for an inline request body schema.\n").
-spec extract_inline_request_body_type(
oaspec@openapi@schema:schema_object(),
binary(),
oaspec@codegen@context:context()
) -> binary().
extract_inline_request_body_type(Schema_obj, Op_id, Ctx) ->
case Schema_obj of
{object_schema, _, _, _, _, _, _} ->
Type_name = <<(oaspec@util@naming:schema_to_type_name(Op_id))/binary,
"RequestBody"/utf8>>,
<<"types."/utf8, Type_name/binary>>;
{all_of_schema, _, _} ->
Type_name@1 = <<(oaspec@util@naming:schema_to_type_name(Op_id))/binary,
"RequestBody"/utf8>>,
<<"types."/utf8, Type_name@1/binary>>;
{string_schema, _, _, _, _, _, _, _} ->
<<"String"/utf8>>;
{integer_schema, _, _, _, _, _} ->
<<"Int"/utf8>>;
{number_schema, _, _, _, _, _} ->
<<"Float"/utf8>>;
{boolean_schema, _, _} ->
<<"Bool"/utf8>>;
_ ->
schema_to_gleam_type(Schema_obj, Ctx)
end.
-file("src/oaspec/codegen/types.gleam", 926).
?DOC(
" Extract the Gleam type for a request body from its content media types.\n"
" Uses types. prefix since request body schemas live in the types module.\n"
).
-spec extract_request_body_type(
oaspec@openapi@spec:request_body(),
binary(),
oaspec@codegen@context:context()
) -> binary().
extract_request_body_type(Rb, Op_id, Ctx) ->
Content_entries = maps:to_list(erlang:element(3, Rb)),
case Content_entries of
[{_, Media_type} | _] ->
case erlang:element(2, Media_type) of
{some, {reference, Ref}} ->
<<"types."/utf8,
(oaspec@util@naming:schema_to_type_name(
oaspec@openapi@resolver:ref_to_name(Ref)
))/binary>>;
{some, {inline, Schema_obj}} ->
extract_inline_request_body_type(Schema_obj, Op_id, Ctx);
_ ->
<<"String"/utf8>>
end;
[] ->
<<"String"/utf8>>
end.
-file("src/oaspec/codegen/types.gleam", 685).
?DOC(" Generate a single request type for an operation.\n").
-spec generate_request_type(
gleam@string_tree:string_tree(),
binary(),
oaspec@openapi@spec:operation(),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_request_type(Sb, Op_id, Operation, Ctx) ->
Type_name = <<(oaspec@util@naming:schema_to_type_name(Op_id))/binary,
"Request"/utf8>>,
Params = erlang:element(6, Operation),
case gleam@list:is_empty(Params) andalso gleam@option:is_none(
erlang:element(7, Operation)
) of
true ->
Sb;
false ->
Sb@1 = case erlang:element(4, Operation) of
{some, Desc} ->
_pipe = Sb,
oaspec@util@string_extra:doc_comment(_pipe, Desc);
none ->
Sb
end,
Sb@2 = begin
_pipe@1 = Sb@1,
oaspec@util@string_extra:line(
_pipe@1,
<<<<"pub type "/utf8, Type_name/binary>>/binary, " {"/utf8>>
)
end,
Sb@3 = begin
_pipe@2 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@2,
1,
<<Type_name/binary, "("/utf8>>
)
end,
Sb@5 = gleam@list:index_fold(
Params,
Sb@3,
fun(Sb@4, Param, Idx) ->
Field_name = oaspec@util@naming:to_snake_case(
erlang:element(2, Param)
),
Field_type = case erlang:element(6, Param) of
{some, {inline, {string_schema, _, _, _, _, _, _, _}}} ->
<<"String"/utf8>>;
{some, {inline, {integer_schema, _, _, _, _, _}}} ->
<<"Int"/utf8>>;
{some, {inline, {number_schema, _, _, _, _, _}}} ->
<<"Float"/utf8>>;
{some, {inline, {boolean_schema, _, _}}} ->
<<"Bool"/utf8>>;
{some, {reference, Ref}} ->
oaspec@util@naming:schema_to_type_name(
oaspec@openapi@resolver:ref_to_name(Ref)
);
_ ->
<<"String"/utf8>>
end,
Final_type = case erlang:element(5, Param) of
true ->
Field_type;
false ->
<<<<"Option("/utf8, Field_type/binary>>/binary,
")"/utf8>>
end,
Has_more = Idx < (erlang:length(Params) - 1),
Has_body = gleam@option:is_some(
erlang:element(7, Operation)
),
Trailing = case Has_more orelse Has_body of
true ->
<<","/utf8>>;
false ->
<<""/utf8>>
end,
_pipe@3 = Sb@4,
oaspec@util@string_extra:indent(
_pipe@3,
2,
<<<<<<Field_name/binary, ": "/utf8>>/binary,
Final_type/binary>>/binary,
Trailing/binary>>
)
end
),
Sb@6 = case erlang:element(7, Operation) of
{some, Rb} ->
Body_type = extract_request_body_type(Rb, Op_id, Ctx),
_pipe@4 = Sb@5,
oaspec@util@string_extra:indent(
_pipe@4,
2,
<<"body: "/utf8, Body_type/binary>>
);
none ->
Sb@5
end,
_pipe@5 = Sb@6,
_pipe@6 = oaspec@util@string_extra:indent(_pipe@5, 1, <<")"/utf8>>),
_pipe@7 = oaspec@util@string_extra:line(_pipe@6, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@7)
end.
-file("src/oaspec/codegen/types.gleam", 625).
?DOC(" Generate request types for all operations.\n").
-spec generate_request_types(oaspec@codegen@context:context()) -> binary().
generate_request_types(Ctx) ->
Operations = collect_operations(Ctx),
Needs_option = gleam@list:any(
Operations,
fun(Op) ->
{_, Operation, _, _} = Op,
gleam@list:any(
erlang:element(6, Operation),
fun(P) -> not erlang:element(5, P) end
)
end
),
Needs_types = gleam@list:any(
Operations,
fun(Op@1) ->
{_, Operation@1, _, _} = Op@1,
Has_ref_params = gleam@list:any(
erlang:element(6, Operation@1),
fun(P@1) -> case erlang:element(6, P@1) of
{some, {reference, _}} ->
true;
_ ->
false
end end
),
Has_typed_body = case erlang:element(7, Operation@1) of
{some, Rb} ->
gleam@list:any(
maps:to_list(erlang:element(3, Rb)),
fun(Ce) ->
{_, Mt} = Ce,
case erlang:element(2, Mt) of
{some, {reference, _}} ->
true;
{some,
{inline, {object_schema, _, _, _, _, _, _}}} ->
true;
{some, {inline, {all_of_schema, _, _}}} ->
true;
_ ->
false
end
end
);
_ ->
false
end,
Has_ref_params orelse Has_typed_body
end
),
Base_imports = case Needs_types of
true ->
[<<(erlang:element(5, erlang:element(3, Ctx)))/binary,
"/types"/utf8>>];
false ->
[]
end,
Imports = case Needs_option of
true ->
[<<"gleam/option.{type Option}"/utf8>> | Base_imports];
false ->
Base_imports
end,
Sb = begin
_pipe = oaspec@util@string_extra:file_header(<<"0.3.0"/utf8>>),
oaspec@util@string_extra:imports(_pipe, Imports)
end,
Sb@2 = gleam@list:fold(
Operations,
Sb,
fun(Sb@1, Op@2) ->
{Op_id, Operation@2, _, _} = Op@2,
generate_request_type(Sb@1, Op_id, Operation@2, Ctx)
end
),
oaspec@util@string_extra:to_string(Sb@2).
-file("src/oaspec/codegen/types.gleam", 969).
?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/types.gleam", 108).
?DOC(" Generate enum types for any properties that have inline enum values.\n").
-spec generate_inline_enums_from_properties(
gleam@string_tree:string_tree(),
binary(),
gleam@dict:dict(binary(), oaspec@openapi@schema:schema_ref()),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_inline_enums_from_properties(Sb, Parent_name, Properties, _) ->
Entries = maps:to_list(Properties),
gleam@list:fold(
Entries,
Sb,
fun(Sb@1, Entry) ->
{Prop_name, Prop_ref} = Entry,
case Prop_ref of
{inline,
{string_schema, Description, _, Enum_values, _, _, _, _}} when Enum_values =/= [] ->
Type_name = <<(oaspec@util@naming:schema_to_type_name(
Parent_name
))/binary,
(oaspec@util@naming:schema_to_type_name(Prop_name))/binary>>,
Sb@2 = maybe_doc_comment(Sb@1, Description),
Sb@3 = begin
_pipe = Sb@2,
oaspec@util@string_extra:line(
_pipe,
<<<<"pub type "/utf8, Type_name/binary>>/binary,
" {"/utf8>>
)
end,
Sb@5 = gleam@list:fold(
Enum_values,
Sb@3,
fun(Sb@4, Value) ->
Variant_name = oaspec@util@naming:schema_to_type_name(
<<<<Type_name/binary, "_"/utf8>>/binary,
Value/binary>>
),
_pipe@1 = Sb@4,
oaspec@util@string_extra:indent(
_pipe@1,
1,
Variant_name
)
end
),
_pipe@2 = Sb@5,
_pipe@3 = oaspec@util@string_extra:line(
_pipe@2,
<<"}"/utf8>>
),
oaspec@util@string_extra:blank_line(_pipe@3);
_ ->
Sb@1
end
end
).
-file("src/oaspec/codegen/types.gleam", 78).
?DOC(
" Generate inline enum types found in object schema properties.\n"
" These are generated as separate types before the parent type.\n"
).
-spec generate_inline_enums_for_schema(
gleam@string_tree:string_tree(),
binary(),
oaspec@openapi@schema:schema_ref(),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_inline_enums_for_schema(Sb, Parent_name, Schema_ref, Ctx) ->
case Schema_ref of
{inline, {object_schema, _, Properties, _, _, _, _}} ->
generate_inline_enums_from_properties(
Sb,
Parent_name,
Properties,
Ctx
);
{inline, {all_of_schema, _, Schemas}} ->
Merged_props = gleam@list:fold(
Schemas,
maps:new(),
fun(Acc, S_ref) -> case S_ref of
{inline, {object_schema, _, Properties@1, _, _, _, _}} ->
maps:merge(Acc, Properties@1);
{reference, _} ->
case oaspec@openapi@resolver:resolve_schema_ref(
S_ref,
erlang:element(2, Ctx)
) of
{ok,
{object_schema, _, Properties@2, _, _, _, _}} ->
maps:merge(Acc, Properties@2);
_ ->
Acc
end;
_ ->
Acc
end end
),
generate_inline_enums_from_properties(
Sb,
Parent_name,
Merged_props,
Ctx
);
_ ->
Sb
end.
-file("src/oaspec/codegen/types.gleam", 161).
?DOC(" Generate a type from a schema object.\n").
-spec generate_schema_type(
gleam@string_tree:string_tree(),
binary(),
binary(),
oaspec@openapi@schema:schema_object(),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_schema_type(Sb, Type_name, Raw_name, Schema, Ctx) ->
case Schema of
{object_schema, Description, Properties, Required, _, _, _} ->
Sb@1 = maybe_doc_comment(Sb, Description),
Sb@2 = begin
_pipe = Sb@1,
oaspec@util@string_extra:line(
_pipe,
<<<<"pub type "/utf8, Type_name/binary>>/binary, " {"/utf8>>
)
end,
Sb@3 = begin
_pipe@1 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@1,
1,
<<Type_name/binary, "("/utf8>>
)
end,
Props = maps:to_list(Properties),
Sb@5 = gleam@list:index_fold(
Props,
Sb@3,
fun(Sb@4, Entry, Idx) ->
{Prop_name, Prop_ref} = Entry,
Field_name = oaspec@util@naming:to_snake_case(Prop_name),
Field_type = schema_ref_to_type_with_inline_enum(
Prop_ref,
Raw_name,
Prop_name,
Ctx
),
Is_required = gleam@list:contains(Required, Prop_name),
Is_already_optional = schema_ref_is_nullable(Prop_ref, Ctx),
Final_type = case {Is_required, Is_already_optional} of
{true, _} ->
Field_type;
{false, true} ->
Field_type;
{false, false} ->
<<<<"Option("/utf8, Field_type/binary>>/binary,
")"/utf8>>
end,
Trailing = case Idx =:= (erlang:length(Props) - 1) of
true ->
<<""/utf8>>;
false ->
<<","/utf8>>
end,
_pipe@2 = Sb@4,
oaspec@util@string_extra:indent(
_pipe@2,
2,
<<<<<<Field_name/binary, ": "/utf8>>/binary,
Final_type/binary>>/binary,
Trailing/binary>>
)
end
),
_pipe@3 = Sb@5,
_pipe@4 = oaspec@util@string_extra:indent(_pipe@3, 1, <<")"/utf8>>),
_pipe@5 = oaspec@util@string_extra:line(_pipe@4, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@5);
{string_schema, Description@1, _, Enum_values, _, _, _, _} when Enum_values =/= [] ->
Sb@6 = maybe_doc_comment(Sb, Description@1),
Sb@7 = begin
_pipe@6 = Sb@6,
oaspec@util@string_extra:line(
_pipe@6,
<<<<"pub type "/utf8, Type_name/binary>>/binary, " {"/utf8>>
)
end,
Sb@9 = gleam@list:fold(
Enum_values,
Sb@7,
fun(Sb@8, Value) ->
Variant_name = oaspec@util@naming:schema_to_type_name(
<<<<Type_name/binary, "_"/utf8>>/binary, Value/binary>>
),
_pipe@7 = Sb@8,
oaspec@util@string_extra:indent(_pipe@7, 1, Variant_name)
end
),
_pipe@8 = Sb@9,
_pipe@9 = oaspec@util@string_extra:line(_pipe@8, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@9);
{one_of_schema, Description@2, Schemas, _} ->
Sb@10 = maybe_doc_comment(Sb, Description@2),
Sb@11 = begin
_pipe@10 = Sb@10,
oaspec@util@string_extra:line(
_pipe@10,
<<<<"pub type "/utf8, Type_name/binary>>/binary, " {"/utf8>>
)
end,
Sb@13 = gleam@list:fold(
Schemas,
Sb@11,
fun(Sb@12, S_ref) ->
Variant_type = schema_ref_to_type(S_ref, Ctx),
Variant_name@1 = <<Type_name/binary, Variant_type/binary>>,
_pipe@11 = Sb@12,
oaspec@util@string_extra:indent(
_pipe@11,
1,
<<<<<<Variant_name@1/binary, "("/utf8>>/binary,
Variant_type/binary>>/binary,
")"/utf8>>
)
end
),
_pipe@12 = Sb@13,
_pipe@13 = oaspec@util@string_extra:line(_pipe@12, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@13);
{all_of_schema, Description@3, Schemas@1} ->
Sb@14 = maybe_doc_comment(Sb, Description@3),
Merged_props = gleam@list:fold(
Schemas@1,
maps:new(),
fun(Acc, S_ref@1) -> case S_ref@1 of
{inline, {object_schema, _, Properties@1, _, _, _, _}} ->
maps:merge(Acc, Properties@1);
{reference, _} ->
case oaspec@openapi@resolver:resolve_schema_ref(
S_ref@1,
erlang:element(2, Ctx)
) of
{ok,
{object_schema, _, Properties@2, _, _, _, _}} ->
maps:merge(Acc, Properties@2);
_ ->
Acc
end;
_ ->
Acc
end end
),
Merged_required = gleam@list:flat_map(
Schemas@1,
fun(S_ref@2) -> case S_ref@2 of
{inline, {object_schema, _, _, Required@1, _, _, _}} ->
Required@1;
{reference, _} ->
case oaspec@openapi@resolver:resolve_schema_ref(
S_ref@2,
erlang:element(2, Ctx)
) of
{ok, {object_schema, _, _, Required@2, _, _, _}} ->
Required@2;
_ ->
[]
end;
_ ->
[]
end end
),
Merged_schema = {object_schema,
Description@3,
Merged_props,
Merged_required,
none,
false,
false},
generate_schema_type(Sb@14, Type_name, Raw_name, Merged_schema, Ctx);
_ ->
Gleam_type = schema_to_gleam_type(Schema, Ctx),
_pipe@14 = Sb,
_pipe@15 = oaspec@util@string_extra:line(
_pipe@14,
<<<<<<"pub type "/utf8, Type_name/binary>>/binary, " = "/utf8>>/binary,
Gleam_type/binary>>
),
oaspec@util@string_extra:blank_line(_pipe@15)
end.
-file("src/oaspec/codegen/types.gleam", 140).
?DOC(" Generate a single type definition.\n").
-spec generate_type_def(
gleam@string_tree:string_tree(),
binary(),
oaspec@openapi@schema:schema_ref(),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_type_def(Sb, Name, Schema_ref, Ctx) ->
Type_name = oaspec@util@naming:schema_to_type_name(Name),
case Schema_ref of
{inline, Schema} ->
generate_schema_type(Sb, Type_name, Name, Schema, Ctx);
{reference, Ref} ->
Resolved_name = oaspec@openapi@resolver:ref_to_name(Ref),
Resolved_type = oaspec@util@naming:schema_to_type_name(
Resolved_name
),
_pipe = Sb,
_pipe@1 = oaspec@util@string_extra:line(
_pipe,
<<<<<<"pub type "/utf8, Type_name/binary>>/binary, " = "/utf8>>/binary,
Resolved_type/binary>>
),
oaspec@util@string_extra:blank_line(_pipe@1)
end.
-file("src/oaspec/codegen/types.gleam", 377).
?DOC(" Generate a named type for an inline schema object.\n").
-spec generate_anonymous_type_for_schema(
gleam@string_tree:string_tree(),
binary(),
binary(),
oaspec@openapi@schema:schema_object(),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_anonymous_type_for_schema(Sb, Op_id, Suffix, Schema_obj, Ctx) ->
Type_name = <<(oaspec@util@naming:schema_to_type_name(Op_id))/binary,
Suffix/binary>>,
Raw_name = <<<<Op_id/binary, "_"/utf8>>/binary, Suffix/binary>>,
case Schema_obj of
{object_schema, _, _, _, _, _, _} ->
generate_schema_type(Sb, Type_name, Raw_name, Schema_obj, Ctx);
{one_of_schema, _, Schemas, _} ->
All_refs = gleam@list:all(Schemas, fun(S) -> case S of
{reference, _} ->
true;
_ ->
false
end end),
case All_refs of
true ->
Sb@1 = begin
_pipe = Sb,
oaspec@util@string_extra:line(
_pipe,
<<<<"pub type "/utf8, Type_name/binary>>/binary,
" {"/utf8>>
)
end,
Sb@3 = gleam@list:fold(
Schemas,
Sb@1,
fun(Sb@2, S_ref) ->
Variant_type = schema_ref_to_type(S_ref, Ctx),
Variant_name = <<Type_name/binary,
Variant_type/binary>>,
_pipe@1 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@1,
1,
<<<<<<Variant_name/binary, "("/utf8>>/binary,
Variant_type/binary>>/binary,
")"/utf8>>
)
end
),
_pipe@2 = Sb@3,
_pipe@3 = oaspec@util@string_extra:line(
_pipe@2,
<<"}"/utf8>>
),
oaspec@util@string_extra:blank_line(_pipe@3);
false ->
Sb
end;
{any_of_schema, _, Schemas@1} ->
All_refs@1 = gleam@list:all(Schemas@1, fun(S@1) -> case S@1 of
{reference, _} ->
true;
_ ->
false
end end),
case All_refs@1 of
true ->
Sb@4 = begin
_pipe@4 = Sb,
oaspec@util@string_extra:line(
_pipe@4,
<<<<"pub type "/utf8, Type_name/binary>>/binary,
" {"/utf8>>
)
end,
Sb@6 = gleam@list:fold(
Schemas@1,
Sb@4,
fun(Sb@5, S_ref@1) ->
Variant_type@1 = schema_ref_to_type(S_ref@1, Ctx),
Variant_name@1 = <<Type_name/binary,
Variant_type@1/binary>>,
_pipe@5 = Sb@5,
oaspec@util@string_extra:indent(
_pipe@5,
1,
<<<<<<Variant_name@1/binary, "("/utf8>>/binary,
Variant_type@1/binary>>/binary,
")"/utf8>>
)
end
),
_pipe@6 = Sb@6,
_pipe@7 = oaspec@util@string_extra:line(
_pipe@6,
<<"}"/utf8>>
),
oaspec@util@string_extra:blank_line(_pipe@7);
false ->
Sb
end;
{all_of_schema, Description, Schemas@2} ->
Merged_props = gleam@list:fold(
Schemas@2,
maps:new(),
fun(Acc, S_ref@2) -> case S_ref@2 of
{inline, {object_schema, _, Properties, _, _, _, _}} ->
maps:merge(Acc, Properties);
{reference, _} ->
case oaspec@openapi@resolver:resolve_schema_ref(
S_ref@2,
erlang:element(2, Ctx)
) of
{ok,
{object_schema, _, Properties@1, _, _, _, _}} ->
maps:merge(Acc, Properties@1);
_ ->
Acc
end;
_ ->
Acc
end end
),
Merged_required = gleam@list:flat_map(
Schemas@2,
fun(S_ref@3) -> case S_ref@3 of
{inline, {object_schema, _, _, Required, _, _, _}} ->
Required;
{reference, _} ->
case oaspec@openapi@resolver:resolve_schema_ref(
S_ref@3,
erlang:element(2, Ctx)
) of
{ok, {object_schema, _, _, Required@1, _, _, _}} ->
Required@1;
_ ->
[]
end;
_ ->
[]
end end
),
Merged_schema = {object_schema,
Description,
Merged_props,
Merged_required,
none,
false,
false},
generate_schema_type(Sb, Type_name, Raw_name, Merged_schema, Ctx);
_ ->
Sb
end.
-file("src/oaspec/codegen/types.gleam", 318).
?DOC(" Generate anonymous types for inline response schemas.\n").
-spec generate_anonymous_response_types(
gleam@string_tree:string_tree(),
binary(),
oaspec@openapi@spec:operation(),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_anonymous_response_types(Sb, Op_id, Operation, Ctx) ->
Responses = maps:to_list(erlang:element(8, Operation)),
gleam@list:fold(
Responses,
Sb,
fun(Sb@1, Entry) ->
{Status_code, Response} = Entry,
Content_entries = maps:to_list(erlang:element(3, Response)),
case Content_entries of
[{_, Media_type} | _] ->
case erlang:element(2, Media_type) of
{some, {inline, Schema_obj}} ->
generate_anonymous_type_for_schema(
Sb@1,
Op_id,
<<"Response"/utf8,
(status_code_suffix(Status_code))/binary>>,
Schema_obj,
Ctx
);
_ ->
Sb@1
end;
_ ->
Sb@1
end
end
).
-file("src/oaspec/codegen/types.gleam", 347).
?DOC(" Generate anonymous type for a request body if it has an inline schema.\n").
-spec generate_anonymous_request_body_type(
gleam@string_tree:string_tree(),
binary(),
oaspec@openapi@spec:operation(),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_anonymous_request_body_type(Sb, Op_id, Operation, Ctx) ->
case erlang:element(7, Operation) of
{some, Rb} ->
Content_entries = maps:to_list(erlang:element(3, Rb)),
case Content_entries of
[{_, Media_type} | _] ->
case erlang:element(2, Media_type) of
{some, {inline, Schema_obj}} ->
generate_anonymous_type_for_schema(
Sb,
Op_id,
<<"RequestBody"/utf8>>,
Schema_obj,
Ctx
);
_ ->
Sb
end;
_ ->
Sb
end;
none ->
Sb
end.
-file("src/oaspec/codegen/types.gleam", 304).
?DOC(" Generate anonymous types from inline schemas in operations.\n").
-spec generate_anonymous_types(
gleam@string_tree:string_tree(),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_anonymous_types(Sb, Ctx) ->
Operations = collect_operations(Ctx),
gleam@list:fold(
Operations,
Sb,
fun(Sb@1, Op) ->
{Op_id, Operation, _, _} = Op,
Sb@2 = generate_anonymous_response_types(
Sb@1,
Op_id,
Operation,
Ctx
),
Sb@3 = generate_anonymous_request_body_type(
Sb@2,
Op_id,
Operation,
Ctx
),
Sb@3
end
).
-file("src/oaspec/codegen/types.gleam", 30).
?DOC(" Generate types from component schemas and anonymous types from operations.\n").
-spec generate_types(oaspec@codegen@context:context()) -> binary().
generate_types(Ctx) ->
Schemas = case erlang:element(5, erlang:element(2, 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,
schema_has_optional_fields(Schema_ref, Ctx)
end
),
Imports = case Needs_option of
true ->
[<<"gleam/option.{type Option}"/utf8>>];
false ->
[]
end,
Sb = begin
_pipe = oaspec@util@string_extra:file_header(<<"0.3.0"/utf8>>),
oaspec@util@string_extra:imports(_pipe, Imports)
end,
Sb@2 = gleam@list:fold(
Schemas,
Sb,
fun(Sb@1, Entry@1) ->
{Name, Schema_ref@1} = Entry@1,
generate_inline_enums_for_schema(Sb@1, Name, Schema_ref@1, Ctx)
end
),
Sb@4 = gleam@list:fold(
Schemas,
Sb@2,
fun(Sb@3, Entry@2) ->
{Name@1, Schema_ref@2} = Entry@2,
generate_type_def(Sb@3, Name@1, Schema_ref@2, Ctx)
end
),
Sb@5 = generate_anonymous_types(Sb@4, Ctx),
oaspec@util@string_extra:to_string(Sb@5).
-file("src/oaspec/codegen/types.gleam", 17).
?DOC(" Generate type definitions from OpenAPI schemas.\n").
-spec generate(oaspec@codegen@context:context()) -> list(oaspec@codegen@context:generated_file()).
generate(Ctx) ->
Types_content = generate_types(Ctx),
Request_types_content = generate_request_types(Ctx),
Response_types_content = generate_response_types(Ctx),
[{generated_file, <<"types.gleam"/utf8>>, Types_content},
{generated_file, <<"request_types.gleam"/utf8>>, Request_types_content},
{generated_file,
<<"response_types.gleam"/utf8>>,
Response_types_content}].