Packages
oaspec
0.17.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@openapi@dedup.erl
-module(oaspec@openapi@dedup).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/oaspec/openapi/dedup.gleam").
-export([dedup_property_names/1, dedup_param_field_names/1, dedup_enum_variants/1, dedup/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-file("src/oaspec/openapi/dedup.gleam", 160).
?DOC(
" Pick the first `base_<n>` suffix that collides neither with another\n"
" literal input name nor with a name this call has already minted.\n"
).
-spec next_unique_name(
binary(),
gleam@dict:dict(binary(), boolean()),
gleam@dict:dict(binary(), boolean()),
integer()
) -> binary().
next_unique_name(Base, Input_names, Claimed, Suffix) ->
Candidate = <<<<Base/binary, "_"/utf8>>/binary,
(erlang:integer_to_binary(Suffix))/binary>>,
case gleam@dict:has_key(Input_names, Candidate) orelse gleam@dict:has_key(
Claimed,
Candidate
) of
true ->
next_unique_name(Base, Input_names, Claimed, Suffix + 1);
false ->
Candidate
end.
-file("src/oaspec/openapi/dedup.gleam", 141).
?DOC(
" Deduplicate a list of strings by appending \"_2\", \"_3\", etc. for duplicates.\n"
" The chosen suffix skips any name that already appears elsewhere in the\n"
" input (so a later literal `foo_2` keeps its label and an earlier\n"
" duplicate `foo` advances to `foo_3`) and any suffix this call has\n"
" already handed out, so the output has no collisions in either\n"
" direction.\n"
).
-spec deduplicate_strings(list(binary())) -> list(binary()).
deduplicate_strings(Names) ->
Input_names = gleam@list:fold(
Names,
maps:new(),
fun(Acc, Name) -> gleam@dict:insert(Acc, Name, true) end
),
{Result_rev, _} = gleam@list:fold(
Names,
{[], maps:new()},
fun(Acc@1, Name@1) ->
{Result, Claimed} = Acc@1,
case gleam@dict:has_key(Claimed, Name@1) of
false ->
{[Name@1 | Result],
gleam@dict:insert(Claimed, Name@1, true)};
true ->
Unique_name = next_unique_name(
Name@1,
Input_names,
Claimed,
2
),
{[Unique_name | Result],
gleam@dict:insert(Claimed, Unique_name, true)}
end
end
),
lists:reverse(Result_rev).
-file("src/oaspec/openapi/dedup.gleam", 100).
?DOC(
" Given a list of original property names (JSON wire names), return a list\n"
" of deduped snake_case Gleam field names. The returned list is parallel\n"
" to the input: result[i] is the Gleam name for input[i].\n"
).
-spec dedup_property_names(list(binary())) -> list(binary()).
dedup_property_names(Prop_names) ->
Snake_names = gleam@list:map(
Prop_names,
fun oaspec@util@naming:to_snake_case/1
),
deduplicate_strings(Snake_names).
-file("src/oaspec/openapi/dedup.gleam", 116).
?DOC(
" Given the parameters of a single operation, return a parallel list of\n"
" deduped snake_case Gleam field names. Parameters whose wire names map to\n"
" the same snake_case field (e.g. `id` in path AND `id` in query) get the\n"
" same `_2`/`_3` suffix treatment used for property names. The reserved\n"
" label `body` is taken first so a parameter literally named `body` is\n"
" renamed instead of clashing with the request type's body field.\n"
"\n"
" The function is order-sensitive: the first occurrence keeps its base\n"
" snake_case form, later occurrences get the suffix. Pass the parameters\n"
" in the same order the spec lists them so type emission, server dispatch,\n"
" and client builder agree on the final field name.\n"
).
-spec dedup_param_field_names(list(oaspec@openapi@spec:parameter(any()))) -> list(binary()).
dedup_param_field_names(Params) ->
Snake_names = gleam@list:map(
Params,
fun(P) -> oaspec@util@naming:to_snake_case(erlang:element(2, P)) end
),
With_body_reserved = [<<"body"/utf8>> | Snake_names],
case deduplicate_strings(With_body_reserved) of
[_ | Rest] ->
Rest;
_ ->
[]
end.
-file("src/oaspec/openapi/dedup.gleam", 130).
?DOC(
" Given a list of original enum values (JSON wire values), return a list\n"
" of deduped PascalCase Gleam variant suffixes. The returned list is\n"
" parallel to the input.\n"
).
-spec dedup_enum_variants(list(binary())) -> list(binary()).
dedup_enum_variants(Enum_values) ->
Pascal_names = gleam@list:map(
Enum_values,
fun oaspec@util@naming:to_pascal_case/1
),
deduplicate_strings(Pascal_names).
-file("src/oaspec/openapi/dedup.gleam", 53).
-spec dedup_schema_object(oaspec@openapi@schema:schema_object()) -> oaspec@openapi@schema:schema_object().
dedup_schema_object(Schema_obj) ->
case Schema_obj of
{object_schema, _, Properties, _, Additional_properties, _, _} = Obj ->
New_props = begin
_pipe = maps:to_list(Properties),
_pipe@1 = gleam@list:map(
_pipe,
fun(Entry) ->
{Name, Prop_ref} = Entry,
{Name, dedup_schema_ref(Prop_ref)}
end
),
maps:from_list(_pipe@1)
end,
{object_schema,
erlang:element(2, Obj),
New_props,
erlang:element(4, Obj),
case Additional_properties of
{typed, Ap} ->
{typed, dedup_schema_ref(Ap)};
forbidden ->
forbidden;
untyped ->
untyped
end,
erlang:element(6, Obj),
erlang:element(7, Obj)};
{one_of_schema, Metadata, Schemas, Discriminator} ->
{one_of_schema,
Metadata,
gleam@list:map(Schemas, fun dedup_schema_ref/1),
Discriminator};
{any_of_schema, Metadata@1, Schemas@1, Discriminator@1} ->
{any_of_schema,
Metadata@1,
gleam@list:map(Schemas@1, fun dedup_schema_ref/1),
Discriminator@1};
_ ->
Schema_obj
end.
-file("src/oaspec/openapi/dedup.gleam", 46).
-spec dedup_schema_ref(oaspec@openapi@schema:schema_ref()) -> oaspec@openapi@schema:schema_ref().
dedup_schema_ref(Schema_ref) ->
case Schema_ref of
{reference, _, _} ->
Schema_ref;
{inline, Schema_obj} ->
{inline, dedup_schema_object(Schema_obj)}
end.
-file("src/oaspec/openapi/dedup.gleam", 27).
?DOC(" Recurse into nested schemas within components (e.g. oneOf children).\n").
-spec dedup_schemas(oaspec@openapi@spec:open_api_spec(IIV)) -> oaspec@openapi@spec:open_api_spec(IIV).
dedup_schemas(Spec) ->
case erlang:element(5, Spec) of
none ->
Spec;
{some, Components} ->
New_schemas = begin
_pipe = maps:to_list(erlang:element(2, Components)),
_pipe@1 = gleam@list:map(
_pipe,
fun(Entry) ->
{Name, Schema_ref} = Entry,
{Name, dedup_schema_ref(Schema_ref)}
end
),
maps:from_list(_pipe@1)
end,
{open_api_spec,
erlang:element(2, Spec),
erlang:element(3, Spec),
erlang:element(4, Spec),
{some,
{components,
New_schemas,
erlang:element(3, Components),
erlang:element(4, Components),
erlang:element(5, Components),
erlang:element(6, Components),
erlang:element(7, Components),
erlang:element(8, Components),
erlang:element(9, Components),
erlang:element(10, Components),
erlang:element(11, Components)}},
erlang:element(6, Spec),
erlang:element(7, Spec),
erlang:element(8, Spec),
erlang:element(9, Spec),
erlang:element(10, Spec),
erlang:element(11, Spec)}
end.
-file("src/oaspec/openapi/dedup.gleam", 21).
?DOC(
" Deduplicate names within schemas to avoid collisions in generated code.\n"
" This is a pre-processing pass that runs after hoisting and before validation.\n"
"\n"
" Scope is intentionally limited: operationId / function-name uniqueness is\n"
" enforced by `oaspec/codegen/validate.gleam` with a hard error, not by a\n"
" silent rename, because renaming mutates the generated public API surface\n"
" without telling the user (see issue #237). Property name and enum variant\n"
" deduplication is done at codegen time via dedup_property_names/1 and\n"
" dedup_enum_variants/1 to preserve JSON wire names.\n"
).
-spec dedup(oaspec@openapi@spec:open_api_spec(IIS)) -> oaspec@openapi@spec:open_api_spec(IIS).
dedup(Spec) ->
_pipe = Spec,
dedup_schemas(_pipe).