Packages
oaspec
0.8.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_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", 301).
?DOC(" Check if any response variant references the types module.\n").
-spec responses_need_types_import(
list({binary(),
oaspec@openapi@spec:operation(oaspec@openapi@spec:spec_stage()),
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) ->
{_, Ref_or} = Entry,
case Ref_or of
{value, Response} ->
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;
_ ->
false
end
end
)
end
).
-file("src/oaspec/codegen/types.gleam", 442).
?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", 365).
?DOC(" Generate a response type for an operation.\n").
-spec generate_response_type(
gleam@string_tree:string_tree(),
binary(),
oaspec@openapi@spec:operation(oaspec@openapi@spec:spec_stage()),
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, Ref_or} = Entry,
case Ref_or of
{value, Response} ->
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;
_ ->
Sb@2
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", 459).
?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", 523).
?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(oaspec@openapi@spec:spec_stage()),
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, Ref_or} = Entry,
case Ref_or of
{value, Path_item} ->
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:filter_map(
erlang:element(6, Operation),
fun(Ref_p) -> case Ref_p of
{value, P} ->
{ok,
{erlang:element(2, P),
erlang:element(3, P)}};
_ ->
{error, nil}
end end
),
Inherited_params = gleam@list:filter(
erlang:element(12, Path_item),
fun(Ref_p@1) -> case Ref_p@1 of
{value, P@1} ->
not gleam@list:contains(
Op_param_keys,
{erlang:element(2, P@1),
erlang:element(
3,
P@1
)}
);
_ ->
true
end 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
end
).
-file("src/oaspec/codegen/types.gleam", 344).
?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.8.0"/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", 601).
?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", 621).
?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", 666).
?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", 642).
?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", 678).
?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", 690).
?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", 703).
?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", 744).
?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", 805).
?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", 785).
?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(oaspec@openapi@spec:spec_stage()),
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", 220).
?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@openapi@spec:spec_stage()),
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, Ref_p, Idx) -> case Ref_p of
{value, Param} ->
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>>
);
_ ->
Sb@4
end end
),
Sb@6 = case erlang:element(7, Operation) of
{some, {value, 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>>
);
_ ->
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(Ref_p) -> case Ref_p of
{value, P} ->
not erlang:element(5, P);
_ ->
false
end end
),
Has_optional_body = case erlang:element(7, Operation) of
{some, {value, 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(Ref_p@1) -> case Ref_p@1 of
{value, P@1} ->
case erlang:element(6, P@1) of
{some, {reference, _, _}} ->
true;
_ ->
false
end;
_ ->
false
end end
),
Has_typed_body = case erlang:element(7, Operation@1) of
{some, {value, 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.8.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", 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}].