Current section

Files

Jump to
oaspec src oaspec@openapi@dedup.erl
Raw

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_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", 111).
-spec apply_deduped_id(
gleam@option:option(oaspec@openapi@spec:operation(HUW)),
binary(),
binary(),
gleam@dict:dict({binary(), binary()}, binary())
) -> gleam@option:option(oaspec@openapi@spec:operation(HUW)).
apply_deduped_id(Op, Path, Method, Id_map) ->
case Op of
none ->
none;
{some, Operation} ->
case gleam_stdlib:map_get(Id_map, {Path, Method}) of
{ok, New_id} when New_id =/= <<""/utf8>> ->
{some,
{operation,
{some, New_id},
erlang:element(3, Operation),
erlang:element(4, Operation),
erlang:element(5, Operation),
erlang:element(6, Operation),
erlang:element(7, Operation),
erlang:element(8, Operation),
erlang:element(9, Operation),
erlang:element(10, Operation),
erlang:element(11, Operation),
erlang:element(12, Operation),
erlang:element(13, Operation)}};
_ ->
{some, Operation}
end
end.
-file("src/oaspec/openapi/dedup.gleam", 93).
-spec dedup_path_item_ops(
oaspec@openapi@spec:path_item(HUR),
binary(),
gleam@dict:dict({binary(), binary()}, binary())
) -> oaspec@openapi@spec:path_item(HUR).
dedup_path_item_ops(Item, Path, Id_map) ->
{path_item,
erlang:element(2, Item),
erlang:element(3, Item),
apply_deduped_id(erlang:element(4, Item), Path, <<"get"/utf8>>, Id_map),
apply_deduped_id(erlang:element(5, Item), Path, <<"post"/utf8>>, Id_map),
apply_deduped_id(erlang:element(6, Item), Path, <<"put"/utf8>>, Id_map),
apply_deduped_id(
erlang:element(7, Item),
Path,
<<"delete"/utf8>>,
Id_map
),
apply_deduped_id(
erlang:element(8, Item),
Path,
<<"patch"/utf8>>,
Id_map
),
apply_deduped_id(erlang:element(9, Item), Path, <<"head"/utf8>>, Id_map),
apply_deduped_id(
erlang:element(10, Item),
Path,
<<"options"/utf8>>,
Id_map
),
apply_deduped_id(
erlang:element(11, Item),
Path,
<<"trace"/utf8>>,
Id_map
),
erlang:element(12, Item),
erlang:element(13, Item)}.
-file("src/oaspec/openapi/dedup.gleam", 129).
?DOC(" Collect all operations as (path, method_str, operation) tuples.\n").
-spec collect_all_operations(oaspec@openapi@spec:open_api_spec(HVD)) -> list({binary(),
binary(),
oaspec@openapi@spec:operation(HVD)}).
collect_all_operations(Spec) ->
Paths = gleam@list:sort(
maps:to_list(erlang:element(4, Spec)),
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
{ref, _} ->
[];
{value, Item} ->
Ops = [{<<"get"/utf8>>, erlang:element(4, Item)},
{<<"post"/utf8>>, erlang:element(5, Item)},
{<<"put"/utf8>>, erlang:element(6, Item)},
{<<"delete"/utf8>>, erlang:element(7, Item)},
{<<"patch"/utf8>>, erlang:element(8, Item)},
{<<"head"/utf8>>, erlang:element(9, Item)},
{<<"options"/utf8>>, erlang:element(10, Item)},
{<<"trace"/utf8>>, erlang:element(11, Item)}],
gleam@list:filter_map(Ops, fun(Op) -> case Op of
{Method, {some, Operation}} ->
{ok, {Path, Method, Operation}};
_ ->
{error, nil}
end end)
end
end
).
-file("src/oaspec/openapi/dedup.gleam", 247).
?DOC(" Deduplicate a list of strings by appending \"_2\", \"_3\", etc. for duplicates.\n").
-spec deduplicate_strings(list(binary())) -> list(binary()).
deduplicate_strings(Names) ->
{Result_rev, _} = gleam@list:fold(
Names,
{[], maps:new()},
fun(Acc, Name) ->
{Result, Counts} = Acc,
case gleam_stdlib:map_get(Counts, Name) of
{error, _} ->
Counts@1 = gleam@dict:insert(Counts, Name, 1),
{[Name | Result], Counts@1};
{ok, Count} ->
New_count = Count + 1,
Unique_name = <<<<Name/binary, "_"/utf8>>/binary,
(erlang:integer_to_binary(New_count))/binary>>,
Counts@2 = gleam@dict:insert(Counts, Name, New_count),
{[Unique_name | Result], Counts@2}
end
end
),
lists:reverse(Result_rev).
-file("src/oaspec/openapi/dedup.gleam", 233).
?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", 241).
?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", 268).
?DOC(" Get element at index from a list.\n").
-spec list_at(list(HVQ), integer()) -> gleam@option:option(HVQ).
list_at(Lst, Idx) ->
case {Lst, Idx} of
{[], _} ->
none;
{[Head | _], 0} ->
{some, Head};
{[_ | Rest], N} ->
list_at(Rest, N - 1)
end.
-file("src/oaspec/openapi/dedup.gleam", 32).
?DOC(
" Deduplicate operationIds across all operations.\n"
" If two operations have the same operationId, the second gets \"_2\" appended, etc.\n"
" Also handles function/type name collisions after case conversion.\n"
).
-spec dedup_operation_ids(oaspec@openapi@spec:open_api_spec(HUO)) -> oaspec@openapi@spec:open_api_spec(HUO).
dedup_operation_ids(Spec) ->
All_ops = collect_all_operations(Spec),
Raw_ids = gleam@list:map(
All_ops,
fun(Op) ->
{_, _, Operation} = Op,
case erlang:element(2, Operation) of
{some, Id} ->
Id;
none ->
<<""/utf8>>
end
end
),
Deduped_ids = deduplicate_strings(Raw_ids),
Fn_names = gleam@list:map(
Deduped_ids,
fun oaspec@util@naming:operation_to_function_name/1
),
Deduped_fn_names = deduplicate_strings(Fn_names),
Indexed_ops = gleam@list:index_map(
All_ops,
fun(Op@1, Idx) -> {Idx, Op@1} end
),
Id_map = gleam@list:index_fold(
Indexed_ops,
maps:new(),
fun(Acc, Entry, _) ->
{Idx@1, {Path, Method, _}} = Entry,
Final_id = case {list_at(Deduped_ids, Idx@1),
list_at(Deduped_fn_names, Idx@1)} of
{{some, Raw_id}, {some, Fn_name}} ->
Expected_fn = oaspec@util@naming:operation_to_function_name(
Raw_id
),
case Expected_fn =:= Fn_name of
true ->
Raw_id;
false ->
Fn_name
end;
{{some, Raw_id@1}, _} ->
Raw_id@1;
{_, _} ->
<<""/utf8>>
end,
gleam@dict:insert(Acc, {Path, Method}, Final_id)
end
),
New_paths = begin
_pipe = maps:to_list(erlang:element(4, Spec)),
_pipe@1 = gleam@list:map(
_pipe,
fun(Entry@1) ->
{Path@1, Ref_or} = Entry@1,
case Ref_or of
{value, Path_item} ->
New_item = dedup_path_item_ops(
Path_item,
Path@1,
Id_map
),
{Path@1, {value, New_item}};
{ref, _} = R ->
{Path@1, R}
end
end
),
maps:from_list(_pipe@1)
end,
{open_api_spec,
erlang:element(2, Spec),
erlang:element(3, Spec),
New_paths,
erlang:element(5, Spec),
erlang:element(6, Spec),
erlang:element(7, Spec),
erlang:element(8, Spec),
erlang:element(9, Spec),
erlang:element(10, Spec),
erlang:element(11, Spec)}.
-file("src/oaspec/openapi/dedup.gleam", 187).
-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
{some, Ap} ->
{some, dedup_schema_ref(Ap)};
none ->
none
end,
erlang:element(6, Obj),
erlang:element(7, Obj),
erlang:element(8, 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", 180).
-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", 161).
?DOC(" Recurse into nested schemas within components (e.g. oneOf children).\n").
-spec dedup_schemas(oaspec@openapi@spec:open_api_spec(HVH)) -> oaspec@openapi@spec:open_api_spec(HVH).
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(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", 23).
?DOC(
" Deduplicate names in the spec to avoid collisions in generated code.\n"
" This is a pre-processing pass that runs after hoisting and before validation.\n"
" It handles:\n"
" - Duplicate operationIds across operations\n"
" - Function/type name collisions after case conversion of operationIds\n"
" Property name and enum variant deduplication is done at codegen time via\n"
" dedup_property_names/1 and dedup_enum_variants/1 to preserve JSON wire names.\n"
).
-spec dedup(oaspec@openapi@spec:open_api_spec(HUL)) -> oaspec@openapi@spec:open_api_spec(HUL).
dedup(Spec) ->
Spec@1 = dedup_operation_ids(Spec),
Spec@2 = dedup_schemas(Spec@1),
Spec@2.