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_ref_to_type/2, schema_to_gleam_type/2, merge_allof_schemas/2, collect_operations/1, schema_has_additional_properties/2, schema_has_untyped_additional_properties/2, schema_has_optional_fields/2, schema_ref_is_read_only/2, schema_ref_is_write_only/2, filter_read_only_properties/2, filter_write_only_properties/2, generate/1]).
-export_type([merged_all_of/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type merged_all_of() :: {merged_all_of,
gleam@dict:dict(binary(), oaspec@openapi@schema:schema_ref()),
list(binary()),
gleam@option:option(oaspec@openapi@schema:schema_ref()),
boolean()}.
-file("src/oaspec/codegen/types.gleam", 48).
?DOC(
" Generate types from component schemas and anonymous types from operations.\n"
" Delegates to the IR pipeline: build IR declarations, then render to source.\n"
).
-spec generate_types(oaspec@codegen@context:context()) -> binary().
generate_types(Ctx) ->
_pipe = oaspec@codegen@ir_build:build_types_module(Ctx),
oaspec@codegen@ir_render:render(_pipe).
-file("src/oaspec/codegen/types.gleam", 131).
?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, _) ->
case Ref of
{inline, Schema} ->
oaspec@codegen@schema_dispatch:schema_type(Schema);
{reference, _, Name} ->
oaspec@util@naming:schema_to_type_name(Name)
end.
-file("src/oaspec/codegen/types.gleam", 140).
?DOC(
" Convert a schema object to a Gleam type string.\n"
" Delegates to schema_dispatch for the centralized type mapping.\n"
).
-spec schema_to_gleam_type(
oaspec@openapi@schema:schema_object(),
oaspec@codegen@context:context()
) -> binary().
schema_to_gleam_type(Schema, _) ->
oaspec@codegen@schema_dispatch:schema_type(Schema).
-file("src/oaspec/codegen/types.gleam", 71).
?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, _, Name} ->
<<<<"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", 55).
?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, _, Name} ->
<<"types."/utf8,
(oaspec@util@naming:schema_to_type_name(Name))/binary>>
end.
-file("src/oaspec/codegen/types.gleam", 287).
?DOC(" Check if any response variant references the types module.\n").
-spec responses_need_types_import(
list({binary(),
oaspec@openapi@spec:operation(),
binary(),
oaspec@openapi@spec:http_method()}),
oaspec@codegen@context:context()
) -> boolean().
responses_need_types_import(Operations, _) ->
gleam@list:any(
Operations,
fun(Op) ->
{_, Operation, _, _} = Op,
Responses = maps:to_list(erlang:element(8, Operation)),
gleam@list:any(
Responses,
fun(Entry) ->
{_, Response} = Entry,
Content_entries = maps:to_list(erlang:element(3, Response)),
case Content_entries of
[] ->
false;
[_, _ | _] ->
false;
[{Media_type_name, Media_type}] ->
case Media_type_name of
<<"text/plain"/utf8>> ->
false;
<<"application/xml"/utf8>> ->
false;
<<"text/xml"/utf8>> ->
false;
<<"application/octet-stream"/utf8>> ->
false;
_ ->
case erlang:element(2, Media_type) of
{some, {reference, _, _}} ->
true;
{some,
{inline,
{array_schema,
_,
{reference, _, _},
_,
_,
_}}} ->
true;
{some,
{inline,
{object_schema,
_,
_,
_,
_,
_,
_,
_}}} ->
true;
{some,
{inline, {one_of_schema, _, _, _}}} ->
true;
{some,
{inline, {any_of_schema, _, _, _}}} ->
true;
{some, {inline, {all_of_schema, _, _}}} ->
true;
_ ->
false
end
end
end
end
)
end
).
-file("src/oaspec/codegen/types.gleam", 408).
?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) ->
<<Type_name/binary, (oaspec@util@http:status_code_suffix(Code))/binary>>.
-file("src/oaspec/codegen/types.gleam", 344).
?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
);
[_, _ | _] ->
_pipe@2 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@2,
1,
<<Variant_name/binary, "(String)"/utf8>>
);
[{Media_type_name, Media_type}] ->
case Media_type_name of
<<"text/plain"/utf8>> ->
case erlang:element(2, Media_type) of
{some, _} ->
_pipe@3 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@3,
1,
<<Variant_name/binary,
"(String)"/utf8>>
);
none ->
_pipe@4 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@4,
1,
Variant_name
)
end;
<<"application/xml"/utf8>> ->
case erlang:element(2, Media_type) of
{some, _} ->
_pipe@3 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@3,
1,
<<Variant_name/binary,
"(String)"/utf8>>
);
none ->
_pipe@4 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@4,
1,
Variant_name
)
end;
<<"text/xml"/utf8>> ->
case erlang:element(2, Media_type) of
{some, _} ->
_pipe@3 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@3,
1,
<<Variant_name/binary,
"(String)"/utf8>>
);
none ->
_pipe@4 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@4,
1,
Variant_name
)
end;
<<"application/octet-stream"/utf8>> ->
case erlang:element(2, Media_type) of
{some, _} ->
_pipe@3 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@3,
1,
<<Variant_name/binary,
"(String)"/utf8>>
);
none ->
_pipe@4 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@4,
1,
Variant_name
)
end;
_ ->
case erlang:element(2, Media_type) of
{some, Ref} ->
Suffix = <<"Response"/utf8,
(oaspec@util@http:status_code_suffix(
Status_code
))/binary>>,
Inner_type = schema_ref_to_type_qualified(
Ref,
Op_id,
Suffix,
Ctx
),
_pipe@5 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@5,
1,
<<<<<<Variant_name/binary,
"("/utf8>>/binary,
Inner_type/binary>>/binary,
")"/utf8>>
);
none ->
_pipe@6 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@6,
1,
Variant_name
)
end
end
end
end
),
_pipe@7 = Sb@3,
_pipe@8 = oaspec@util@string_extra:line(_pipe@7, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@8)
end.
-file("src/oaspec/codegen/types.gleam", 425).
?DOC(
" Merge allOf sub-schemas: properties, required, and additionalProperties.\n"
" Non-object sub-schemas (primitives, arrays) are included as a synthetic\n"
" \"value\" property to preserve their constraints.\n"
).
-spec merge_allof_schemas(
list(oaspec@openapi@schema:schema_ref()),
oaspec@codegen@context:context()
) -> merged_all_of().
merge_allof_schemas(Schemas, Ctx) ->
gleam@list:index_fold(
Schemas,
{merged_all_of, maps:new(), [], none, false},
fun(Acc, S_ref, Idx) ->
Resolved = case S_ref of
{inline, Obj} ->
{ok, Obj};
{reference, _, _} ->
oaspec@openapi@resolver:resolve_schema_ref(
S_ref,
erlang:element(2, Ctx)
)
end,
case Resolved of
{ok,
{object_schema,
_,
Properties,
Required,
Additional_properties,
Additional_properties_untyped,
_,
_}} ->
Merged_ap = case {erlang:element(4, Acc),
Additional_properties} of
{none, Ap} ->
Ap;
{Existing, _} ->
Existing
end,
Merged_ap_untyped = erlang:element(5, Acc) orelse Additional_properties_untyped,
{merged_all_of,
maps:merge(erlang:element(2, Acc), Properties),
lists:append(erlang:element(3, Acc), Required),
Merged_ap,
Merged_ap_untyped};
{ok, Schema_obj} ->
Field_name = case Idx of
0 ->
<<"value"/utf8>>;
N ->
<<"value_"/utf8,
(erlang:integer_to_binary(N))/binary>>
end,
{merged_all_of,
gleam@dict:insert(
erlang:element(2, Acc),
Field_name,
{inline, Schema_obj}
),
[Field_name | erlang:element(3, Acc)],
erlang:element(4, Acc),
erlang:element(5, Acc)};
_ ->
Acc
end
end
).
-file("src/oaspec/codegen/types.gleam", 489).
?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},
{erlang:element(9, Path_item), head},
{erlang:element(10, Path_item), options},
{erlang:element(11, Path_item), trace}],
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(12, 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},
erlang:element(11, Operation),
erlang:element(12, Operation),
erlang:element(13, Operation)},
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", 323).
?DOC(" Generate response types for all operations.\n").
-spec generate_response_types(oaspec@codegen@context:context()) -> binary().
generate_response_types(Ctx) ->
Operations = collect_operations(Ctx),
Needs_types = responses_need_types_import(Operations, Ctx),
Imports = case Needs_types of
true ->
[<<(erlang:element(5, erlang:element(3, Ctx)))/binary,
"/types"/utf8>>];
false ->
[]
end,
Sb = begin
_pipe = oaspec@util@string_extra:file_header(<<"0.6.1"/utf8>>),
oaspec@util@string_extra:imports(_pipe, Imports)
end,
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", 554).
?DOC(" Check if a schema has typed or untyped additionalProperties that would need Dict.\n").
-spec schema_has_additional_properties(
oaspec@openapi@schema:schema_ref(),
oaspec@codegen@context:context()
) -> boolean().
schema_has_additional_properties(Schema_ref, Ctx) ->
case Schema_ref of
{inline, {object_schema, _, _, _, {some, _}, _, _, _}} ->
true;
{inline, {object_schema, _, _, _, _, true, _, _}} ->
true;
{inline, {all_of_schema, _, Schemas}} ->
gleam@list:any(
Schemas,
fun(S) -> schema_has_additional_properties(S, Ctx) end
);
{reference, _, _} ->
case oaspec@openapi@resolver:resolve_schema_ref(
Schema_ref,
erlang:element(2, Ctx)
) of
{ok, Schema_obj} ->
schema_has_additional_properties({inline, Schema_obj}, Ctx);
{error, _} ->
false
end;
_ ->
false
end.
-file("src/oaspec/codegen/types.gleam", 574).
?DOC(" Check if a schema has untyped additionalProperties (needs Dynamic import).\n").
-spec schema_has_untyped_additional_properties(
oaspec@openapi@schema:schema_ref(),
oaspec@codegen@context:context()
) -> boolean().
schema_has_untyped_additional_properties(Schema_ref, Ctx) ->
case Schema_ref of
{inline, {object_schema, _, _, _, _, true, _, _}} ->
true;
{inline, {all_of_schema, _, Schemas}} ->
gleam@list:any(
Schemas,
fun(S) -> schema_has_untyped_additional_properties(S, Ctx) end
);
{reference, _, _} ->
case oaspec@openapi@resolver:resolve_schema_ref(
Schema_ref,
erlang:element(2, Ctx)
) of
{ok, Schema_obj} ->
schema_has_untyped_additional_properties(
{inline, Schema_obj},
Ctx
);
{error, _} ->
false
end;
_ ->
false
end.
-file("src/oaspec/codegen/types.gleam", 619).
?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", 595).
?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", 631).
?DOC(" Check if a SchemaRef has readOnly metadata, resolving $ref if needed.\n").
-spec schema_ref_is_read_only(
oaspec@openapi@schema:schema_ref(),
oaspec@codegen@context:context()
) -> boolean().
schema_ref_is_read_only(Ref, Ctx) ->
case Ref of
{inline, S} ->
erlang:element(6, oaspec@openapi@schema:get_metadata(S));
{reference, _, _} ->
case oaspec@openapi@resolver:resolve_schema_ref(
Ref,
erlang:element(2, Ctx)
) of
{ok, S@1} ->
erlang:element(6, oaspec@openapi@schema:get_metadata(S@1));
{error, _} ->
false
end
end.
-file("src/oaspec/codegen/types.gleam", 643).
?DOC(" Check if a SchemaRef has writeOnly metadata, resolving $ref if needed.\n").
-spec schema_ref_is_write_only(
oaspec@openapi@schema:schema_ref(),
oaspec@codegen@context:context()
) -> boolean().
schema_ref_is_write_only(Ref, Ctx) ->
case Ref of
{inline, S} ->
erlang:element(7, oaspec@openapi@schema:get_metadata(S));
{reference, _, _} ->
case oaspec@openapi@resolver:resolve_schema_ref(
Ref,
erlang:element(2, Ctx)
) of
{ok, S@1} ->
erlang:element(7, oaspec@openapi@schema:get_metadata(S@1));
{error, _} ->
false
end
end.
-file("src/oaspec/codegen/types.gleam", 656).
?DOC(
" Filter readOnly properties from an ObjectSchema for request body context.\n"
" Returns a new schema with readOnly properties removed.\n"
).
-spec filter_read_only_properties(
oaspec@openapi@schema:schema_object(),
oaspec@codegen@context:context()
) -> oaspec@openapi@schema:schema_object().
filter_read_only_properties(Schema_obj, Ctx) ->
case Schema_obj of
{object_schema,
Metadata,
Properties,
Required,
Additional_properties,
Additional_properties_untyped,
Min_properties,
Max_properties} ->
Filtered_props = gleam@dict:filter(
Properties,
fun(_, Prop_ref) ->
not schema_ref_is_read_only(Prop_ref, Ctx)
end
),
Filtered_required = gleam@list:filter(
Required,
fun(Name) -> case gleam_stdlib:map_get(Filtered_props, Name) of
{ok, _} ->
true;
{error, _} ->
false
end end
),
{object_schema,
Metadata,
Filtered_props,
Filtered_required,
Additional_properties,
Additional_properties_untyped,
Min_properties,
Max_properties};
_ ->
Schema_obj
end.
-file("src/oaspec/codegen/types.gleam", 697).
?DOC(
" Filter writeOnly properties from an ObjectSchema for response body context.\n"
" Returns a new schema with writeOnly properties removed.\n"
).
-spec filter_write_only_properties(
oaspec@openapi@schema:schema_object(),
oaspec@codegen@context:context()
) -> oaspec@openapi@schema:schema_object().
filter_write_only_properties(Schema_obj, Ctx) ->
case Schema_obj of
{object_schema,
Metadata,
Properties,
Required,
Additional_properties,
Additional_properties_untyped,
Min_properties,
Max_properties} ->
Filtered_props = gleam@dict:filter(
Properties,
fun(_, Prop_ref) ->
not schema_ref_is_write_only(Prop_ref, Ctx)
end
),
Filtered_required = gleam@list:filter(
Required,
fun(Name) -> case gleam_stdlib:map_get(Filtered_props, Name) of
{ok, _} ->
true;
{error, _} ->
false
end end
),
{object_schema,
Metadata,
Filtered_props,
Filtered_required,
Additional_properties,
Additional_properties_untyped,
Min_properties,
Max_properties};
_ ->
Schema_obj
end.
-file("src/oaspec/codegen/types.gleam", 758).
?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", 738).
?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, _, Name}} ->
<<"types."/utf8,
(oaspec@util@naming:schema_to_type_name(Name))/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", 211).
?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, {inline, {array_schema, _, Items, _, _, _}}} ->
Item_type = case Items of
{inline, {string_schema, _, _, _, _, _, _}} ->
<<"String"/utf8>>;
{inline, {integer_schema, _, _, _, _, _, _, _}} ->
<<"Int"/utf8>>;
{inline, {number_schema, _, _, _, _, _, _, _}} ->
<<"Float"/utf8>>;
{inline, {boolean_schema, _}} ->
<<"Bool"/utf8>>;
{reference, _, Name} ->
oaspec@util@naming:schema_to_type_name(Name);
_ ->
<<"String"/utf8>>
end,
<<<<"List("/utf8, Item_type/binary>>/binary,
")"/utf8>>;
{some, {reference, _, Name@1}} ->
<<"types."/utf8,
(oaspec@util@naming:schema_to_type_name(Name@1))/binary>>;
_ ->
<<"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),
Wrapped = case erlang:element(4, Rb) of
true ->
Body_type;
false ->
<<<<"Option("/utf8, Body_type/binary>>/binary,
")"/utf8>>
end,
_pipe@4 = Sb@5,
oaspec@util@string_extra:indent(
_pipe@4,
2,
<<"body: "/utf8, Wrapped/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", 145).
?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,
Has_optional_params = gleam@list:any(
erlang:element(6, Operation),
fun(P) -> not erlang:element(5, P) end
),
Has_optional_body = case erlang:element(7, Operation) of
{some, Rb} ->
not erlang:element(4, Rb);
_ ->
false
end,
Has_optional_params orelse Has_optional_body
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@1} ->
gleam@list:any(
maps:to_list(erlang:element(3, Rb@1)),
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.6.1"/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", 22).
?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, shared_target},
{generated_file,
<<"request_types.gleam"/utf8>>,
Request_types_content,
shared_target},
{generated_file,
<<"response_types.gleam"/utf8>>,
Response_types_content,
shared_target}].