Packages
oaspec
0.4.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, merge_allof_schemas/2, collect_operations/1, schema_has_additional_properties/2, schema_has_untyped_additional_properties/2, schema_has_optional_fields/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", 579).
?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", 507).
?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", 490).
?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", 568).
?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", 320).
?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", 823).
?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", 765).
?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_name, Media_type} | _] ->
case Media_type_name of
<<"text/plain"/utf8>> ->
case erlang:element(2, Media_type) of
{some, _} ->
_pipe@2 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@2,
1,
<<Variant_name/binary,
"(String)"/utf8>>
);
none ->
_pipe@3 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@3,
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@4 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@4,
1,
<<<<<<Variant_name/binary,
"("/utf8>>/binary,
Inner_type/binary>>/binary,
")"/utf8>>
);
none ->
_pipe@5 = Sb@2,
oaspec@util@string_extra:indent(
_pipe@5,
1,
Variant_name
)
end
end
end
end
),
_pipe@6 = Sb@3,
_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", 838).
?DOC(" Merge allOf sub-schemas: properties, required, and additionalProperties.\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:fold(
Schemas,
{merged_all_of, maps:new(), [], none, false},
fun(Acc, S_ref) ->
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};
_ ->
Acc
end
end
).
-file("src/oaspec/codegen/types.gleam", 886).
?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", 748).
?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.4.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", 948).
?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", 968).
?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", 1013).
?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", 989).
?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", 1046).
?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", 1026).
?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", 675).
?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, Ref} ->
oaspec@util@naming:schema_to_type_name(
oaspec@openapi@resolver:ref_to_name(Ref)
);
_ ->
<<"String"/utf8>>
end,
<<<<"List("/utf8, Item_type/binary>>/binary,
")"/utf8>>;
{some, {reference, Ref@1}} ->
oaspec@util@naming:schema_to_type_name(
oaspec@openapi@resolver:ref_to_name(Ref@1)
);
_ ->
<<"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", 615).
?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.4.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", 1069).
?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", 137).
?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", 107).
?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", 190).
?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,
Additional_properties,
Additional_properties_untyped,
_} ->
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),
Has_additional_props = gleam@option:is_some(Additional_properties)
orelse Additional_properties_untyped,
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,
Is_last = Idx =:= (erlang:length(Props) - 1),
Trailing = case Is_last andalso not Has_additional_props 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
),
Sb@6 = case {Additional_properties, Additional_properties_untyped} of
{{some, Ap_ref}, _} ->
Inner_type = schema_ref_to_type(Ap_ref, Ctx),
_pipe@3 = Sb@5,
oaspec@util@string_extra:indent(
_pipe@3,
2,
<<<<"additional_properties: Dict(String, "/utf8,
Inner_type/binary>>/binary,
")"/utf8>>
);
{none, true} ->
_pipe@4 = Sb@5,
oaspec@util@string_extra:indent(
_pipe@4,
2,
<<"additional_properties: Dict(String, Dynamic)"/utf8>>
);
{none, false} ->
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);
{string_schema, Description@1, _, Enum_values, _, _, _, _} when Enum_values =/= [] ->
Sb@7 = maybe_doc_comment(Sb, Description@1),
Sb@8 = begin
_pipe@8 = Sb@7,
oaspec@util@string_extra:line(
_pipe@8,
<<<<"pub type "/utf8, Type_name/binary>>/binary, " {"/utf8>>
)
end,
Sb@10 = gleam@list:fold(
Enum_values,
Sb@8,
fun(Sb@9, Value) ->
Variant_name = oaspec@util@naming:schema_to_type_name(
<<<<Type_name/binary, "_"/utf8>>/binary, Value/binary>>
),
_pipe@9 = Sb@9,
oaspec@util@string_extra:indent(_pipe@9, 1, Variant_name)
end
),
_pipe@10 = Sb@10,
_pipe@11 = oaspec@util@string_extra:line(_pipe@10, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@11);
{one_of_schema, Description@2, Schemas, _} ->
Sb@11 = maybe_doc_comment(Sb, Description@2),
Sb@12 = begin
_pipe@12 = Sb@11,
oaspec@util@string_extra:line(
_pipe@12,
<<<<"pub type "/utf8, Type_name/binary>>/binary, " {"/utf8>>
)
end,
Sb@14 = gleam@list:fold(
Schemas,
Sb@12,
fun(Sb@13, S_ref) ->
Variant_type = schema_ref_to_type(S_ref, Ctx),
Variant_name@1 = <<Type_name/binary, Variant_type/binary>>,
_pipe@13 = Sb@13,
oaspec@util@string_extra:indent(
_pipe@13,
1,
<<<<<<Variant_name@1/binary, "("/utf8>>/binary,
Variant_type/binary>>/binary,
")"/utf8>>
)
end
),
_pipe@14 = Sb@14,
_pipe@15 = oaspec@util@string_extra:line(_pipe@14, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@15);
{all_of_schema, Description@3, Schemas@1} ->
Sb@15 = maybe_doc_comment(Sb, Description@3),
Merged = merge_allof_schemas(Schemas@1, Ctx),
Merged_schema = {object_schema,
Description@3,
erlang:element(2, Merged),
erlang:element(3, Merged),
erlang:element(4, Merged),
erlang:element(5, Merged),
false},
generate_schema_type(Sb@15, Type_name, Raw_name, Merged_schema, Ctx);
_ ->
Gleam_type = schema_to_gleam_type(Schema, Ctx),
_pipe@16 = Sb,
_pipe@17 = oaspec@util@string_extra:line(
_pipe@16,
<<<<<<"pub type "/utf8, Type_name/binary>>/binary, " = "/utf8>>/binary,
Gleam_type/binary>>
),
oaspec@util@string_extra:blank_line(_pipe@17)
end.
-file("src/oaspec/codegen/types.gleam", 169).
?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", 409).
?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 = merge_allof_schemas(Schemas@2, Ctx),
Merged_schema = {object_schema,
Description,
erlang:element(2, Merged),
erlang:element(3, Merged),
erlang:element(4, Merged),
erlang:element(5, Merged),
false},
generate_schema_type(Sb, Type_name, Raw_name, Merged_schema, Ctx);
_ ->
Sb
end.
-file("src/oaspec/codegen/types.gleam", 350).
?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,
(oaspec@util@http:status_code_suffix(
Status_code
))/binary>>,
Schema_obj,
Ctx
);
_ ->
Sb@1
end;
_ ->
Sb@1
end
end
).
-file("src/oaspec/codegen/types.gleam", 379).
?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", 336).
?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", 31).
?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
),
Needs_dict = gleam@list:any(
Schemas,
fun(Entry@1) ->
{_, Schema_ref@1} = Entry@1,
schema_has_additional_properties(Schema_ref@1, Ctx)
end
),
Needs_dynamic = gleam@list:any(
Schemas,
fun(Entry@2) ->
{_, Schema_ref@2} = Entry@2,
schema_has_untyped_additional_properties(Schema_ref@2, Ctx)
end
),
Imports = case {Needs_option, Needs_dict, Needs_dynamic} of
{true, true, true} ->
[<<"gleam/dict.{type Dict}"/utf8>>,
<<"gleam/dynamic.{type Dynamic}"/utf8>>,
<<"gleam/option.{type Option}"/utf8>>];
{true, true, false} ->
[<<"gleam/dict.{type Dict}"/utf8>>,
<<"gleam/option.{type Option}"/utf8>>];
{true, false, _} ->
[<<"gleam/option.{type Option}"/utf8>>];
{false, true, true} ->
[<<"gleam/dict.{type Dict}"/utf8>>,
<<"gleam/dynamic.{type Dynamic}"/utf8>>];
{false, true, false} ->
[<<"gleam/dict.{type Dict}"/utf8>>];
{false, false, _} ->
[]
end,
Sb = begin
_pipe = oaspec@util@string_extra:file_header(<<"0.4.0"/utf8>>),
oaspec@util@string_extra:imports(_pipe, Imports)
end,
Sb@2 = gleam@list:fold(
Schemas,
Sb,
fun(Sb@1, Entry@3) ->
{Name, Schema_ref@3} = Entry@3,
generate_inline_enums_for_schema(Sb@1, Name, Schema_ref@3, Ctx)
end
),
Sb@4 = gleam@list:fold(
Schemas,
Sb@2,
fun(Sb@3, Entry@4) ->
{Name@1, Schema_ref@4} = Entry@4,
generate_type_def(Sb@3, Name@1, Schema_ref@4, Ctx)
end
),
Sb@5 = generate_anonymous_types(Sb@4, Ctx),
oaspec@util@string_extra:to_string(Sb@5).
-file("src/oaspec/codegen/types.gleam", 18).
?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}].