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@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.