Packages
oaspec
0.3.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@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}].