-module(oaspec@util@naming). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/oaspec/util/naming.gleam"). -export([capitalize/1, to_pascal_case/1, schema_to_type_name/1, to_snake_case/1, operation_to_function_name/1, to_module_name/1, deduplicate_names/1]). -export_type([regexes/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 regexes() :: {regexes, gleam@regexp:regexp(), gleam@regexp:regexp(), gleam@regexp:regexp()}. -file("src/oaspec/util/naming.gleam", 17). -spec compile_regexes() -> regexes(). compile_regexes() -> Word_separator@1 = case gleam@regexp:from_string(<<"[_\\-\\s./]+"/utf8>>) of {ok, Word_separator} -> Word_separator; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"oaspec/util/naming"/utf8>>, function => <<"compile_regexes"/utf8>>, line => 18, value => _assert_fail, start => 394, 'end' => 460, pattern_start => 405, pattern_end => 423}) end, Camel_case@1 = case gleam@regexp:from_string( <<"([A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+|[0-9]+)"/utf8>> ) of {ok, Camel_case} -> Camel_case; _assert_fail@1 -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"oaspec/util/naming"/utf8>>, function => <<"compile_regexes"/utf8>>, line => 19, value => _assert_fail@1, start => 463, 'end' => 566, pattern_start => 474, pattern_end => 488}) end, Underscore_before_caps@1 = case gleam@regexp:from_string( <<"([a-z0-9])([A-Z])"/utf8>> ) of {ok, Underscore_before_caps} -> Underscore_before_caps; _assert_fail@2 -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"oaspec/util/naming"/utf8>>, function => <<"compile_regexes"/utf8>>, line => 21, value => _assert_fail@2, start => 569, 'end' => 652, pattern_start => 580, pattern_end => 606}) end, {regexes, Word_separator@1, Camel_case@1, Underscore_before_caps@1}. -file("src/oaspec/util/naming.gleam", 51). ?DOC(" Gleam reserved keywords that cannot be used as identifiers.\n"). -spec escape_keyword(binary()) -> binary(). escape_keyword(Name) -> case Name of <<"as"/utf8>> -> <>; <<"assert"/utf8>> -> <>; <<"auto"/utf8>> -> <>; <<"case"/utf8>> -> <>; <<"const"/utf8>> -> <>; <<"external"/utf8>> -> <>; <<"fn"/utf8>> -> <>; <<"if"/utf8>> -> <>; <<"import"/utf8>> -> <>; <<"let"/utf8>> -> <>; <<"opaque"/utf8>> -> <>; <<"panic"/utf8>> -> <>; <<"pub"/utf8>> -> <>; <<"test"/utf8>> -> <>; <<"todo"/utf8>> -> <>; <<"type"/utf8>> -> <>; <<"use"/utf8>> -> <>; _ -> Name end. -file("src/oaspec/util/naming.gleam", 93). ?DOC(" Capitalize the first letter of a string.\n"). -spec capitalize(binary()) -> binary(). capitalize(Input) -> case gleam_stdlib:string_pop_grapheme(Input) of {ok, {First, Rest}} -> <<(string:uppercase(First))/binary, Rest/binary>>; {error, _} -> Input end. -file("src/oaspec/util/naming.gleam", 109). ?DOC(" Split camelCase/PascalCase into separate words.\n"). -spec split_camel_case(binary(), regexes()) -> list(binary()). split_camel_case(Input, Re) -> Matches = gleam@regexp:scan(erlang:element(3, Re), Input), case Matches of [] -> [Input]; _ -> gleam@list:map( Matches, fun(M) -> {match, Content, _} = M, Content end ) end. -file("src/oaspec/util/naming.gleam", 101). ?DOC(" Split a string into words by common separators.\n"). -spec split_words(binary(), regexes()) -> list(binary()). split_words(Input, Re) -> Parts = gleam@regexp:split(erlang:element(2, Re), Input), _pipe = Parts, _pipe@1 = gleam@list:flat_map( _pipe, fun(_capture) -> split_camel_case(_capture, Re) end ), gleam@list:filter(_pipe@1, fun(S) -> S /= <<""/utf8>> end). -file("src/oaspec/util/naming.gleam", 28). ?DOC( " Convert a string to PascalCase for Gleam type names.\n" " Examples: \"pet_store\" -> \"PetStore\", \"get-user\" -> \"GetUser\"\n" ). -spec to_pascal_case(binary()) -> binary(). to_pascal_case(Input) -> Re = compile_regexes(), _pipe = Input, _pipe@1 = split_words(_pipe, Re), _pipe@2 = gleam@list:map(_pipe@1, fun capitalize/1), gleam@string:join(_pipe@2, <<""/utf8>>). -file("src/oaspec/util/naming.gleam", 81). ?DOC(" Convert an OpenAPI schema name to a valid Gleam type name.\n"). -spec schema_to_type_name(binary()) -> binary(). schema_to_type_name(Schema_name) -> _pipe = Schema_name, to_pascal_case(_pipe). -file("src/oaspec/util/naming.gleam", 122). ?DOC(" Insert underscores before capital letters in camelCase strings.\n"). -spec insert_underscores_before_caps(binary(), regexes()) -> binary(). insert_underscores_before_caps(Input, Re) -> gleam_regexp_ffi:replace(erlang:element(4, Re), Input, <<"\\1_\\2"/utf8>>). -file("src/oaspec/util/naming.gleam", 39). ?DOC( " Convert a string to snake_case for Gleam function/variable names.\n" " Examples: \"PetStore\" -> \"pet_store\", \"getUserById\" -> \"get_user_by_id\"\n" " Gleam keywords are suffixed with _ to avoid syntax errors.\n" ). -spec to_snake_case(binary()) -> binary(). to_snake_case(Input) -> Re = compile_regexes(), Result = begin _pipe = Input, _pipe@1 = insert_underscores_before_caps(_pipe, Re), _pipe@2 = split_words(_pipe@1, Re), _pipe@3 = gleam@list:map(_pipe@2, fun string:lowercase/1), gleam@string:join(_pipe@3, <<"_"/utf8>>) end, escape_keyword(Result). -file("src/oaspec/util/naming.gleam", 75). ?DOC(" Convert an OpenAPI operation ID to a valid Gleam function name.\n"). -spec operation_to_function_name(binary()) -> binary(). operation_to_function_name(Operation_id) -> _pipe = Operation_id, to_snake_case(_pipe). -file("src/oaspec/util/naming.gleam", 87). ?DOC(" Convert an OpenAPI schema name to a valid Gleam module name.\n"). -spec to_module_name(binary()) -> binary(). to_module_name(Name) -> _pipe = Name, to_snake_case(_pipe). -file("src/oaspec/util/naming.gleam", 128). ?DOC( " Deduplicate a list of names by appending _2, _3, etc. to duplicates.\n" " Preserves order. First occurrence keeps original name.\n" ). -spec deduplicate_names(list(binary())) -> list(binary()). deduplicate_names(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).