-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(ICF)), binary(), binary(), gleam@dict:dict({binary(), binary()}, binary()) ) -> gleam@option:option(oaspec@openapi@spec:operation(ICF)). 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(ICA), binary(), gleam@dict:dict({binary(), binary()}, binary()) ) -> oaspec@openapi@spec:path_item(ICA). 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(ICM)) -> list({binary(), binary(), oaspec@openapi@spec:operation(ICM)}). 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", 248). ?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 = <<<>/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", 234). ?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", 242). ?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", 269). ?DOC(" Get element at index from a list.\n"). -spec list_at(list(ICZ), integer()) -> gleam@option:option(ICZ). 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(IBX)) -> oaspec@openapi@spec:open_api_spec(IBX). 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 {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", 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(ICQ)) -> oaspec@openapi@spec:open_api_spec(ICQ). 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(IBU)) -> oaspec@openapi@spec:open_api_spec(IBU). dedup(Spec) -> Spec@1 = dedup_operation_ids(Spec), Spec@2 = dedup_schemas(Spec@1), Spec@2.