-module(ids@nanoid). -compile([no_auto_import, nowarn_unused_vars]). -export([generate/0]). -spec check_size(integer()) -> {ok, boolean()} | {error, binary()}. check_size(Size) -> case Size > 0 of true -> _pipe = true, {ok, _pipe}; false -> Error = <<"Error: The specified ID size is too small. Increase the size of the ID."/utf8>>, _pipe@1 = Error, {error, _pipe@1} end. -spec check_alphabet(bitstring()) -> {ok, boolean()} | {error, binary()}. check_alphabet(Alphabet) -> case gleam@bit_string:byte_size(Alphabet) > 1 of true -> _pipe = true, {ok, _pipe}; false -> Error = <<"Error: The specified alphabet size is too small. Increase the size of the alphabet."/utf8>>, _pipe@1 = Error, {error, _pipe@1} end. -spec check_nanoid_args(integer(), bitstring()) -> {ok, boolean()} | {error, binary()}. check_nanoid_args(Size, Alphabet) -> case check_size(Size) of {ok, true} -> case check_alphabet(Alphabet) of {ok, true} -> _pipe = true, {ok, _pipe}; {error, Error} -> _pipe@1 = Error, {error, _pipe@1} end; {error, Error@1} -> _pipe@2 = Error@1, {error, _pipe@2} end. -spec random_bytes(integer()) -> list(integer()). random_bytes(Size) -> _pipe = crypto:strong_rand_bytes(Size), binary:bin_to_list(_pipe). -spec generate_nanoid(integer(), bitstring(), integer()) -> {ok, bitstring()} | {error, binary()}. generate_nanoid(Size, Alphabet, Mask) -> case check_nanoid_args(Size, Alphabet) of {ok, true} -> _pipe = Size, _pipe@1 = random_bytes(_pipe), _pipe@2 = gleam@list:map( _pipe@1, fun(X) -> case gleam@bit_string:slice( Alphabet, erlang:'band'(X, Mask), 1 ) of {ok, Nanoid} -> Nanoid; _ -> <<""/utf8>> end end ), _pipe@3 = gleam@bit_string:concat(_pipe@2), {ok, _pipe@3}; {error, Error} -> _pipe@4 = Error, {error, _pipe@4} end. -spec do_generate(integer(), bitstring(), integer(), integer(), bitstring()) -> {ok, bitstring()} | {error, binary()}. do_generate(Size, Alphabet, Mask, Step, Acc) -> case gleam@bit_string:byte_size(Acc) >= Size of true -> _assert_subject = gleam@bit_string:slice(Acc, 0, Size), {ok, Nanoid} = case _assert_subject of {ok, _} -> _assert_subject; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"ids/nanoid"/utf8>>, function => <<"do_generate"/utf8>>, line => 82}) end, _pipe = Nanoid, {ok, _pipe}; false -> case generate_nanoid(Step, Alphabet, Mask) of {ok, Partial_nanoid} -> _pipe@1 = gleam@bit_string:concat([Acc, Partial_nanoid]), do_generate(Size, Alphabet, Mask, Step, _pipe@1); {error, Error} -> _pipe@2 = Error, {error, _pipe@2} end end. -spec calculate_mask(integer()) -> integer(). calculate_mask(Alphabet_length) -> V1 = case math:log(2.0) of 0.0 -> 0.0; Gleam@denominator -> math:log(gleam@int:to_float(Alphabet_length - 1)) / Gleam@denominator end, V2 = gleam@float:round(gleam@float:floor(V1)), erlang:'bsl'(2, V2) - 1. -spec calculate_step(integer(), integer(), integer()) -> integer(). calculate_step(Mask, Size, Alphabet_length) -> Step = gleam@float:ceiling(case gleam@int:to_float(Alphabet_length) of 0.0 -> 0.0; Gleam@denominator -> ((1.6 * gleam@int:to_float(Mask)) * gleam@int:to_float( Size )) / Gleam@denominator end), gleam@float:round(Step). -spec generate() -> binary(). generate() -> Alphabet = <<"_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"/utf8>>, _assert_subject = gleam@bit_string:to_string(Alphabet), {ok, Alphabet_string} = case _assert_subject of {ok, _} -> _assert_subject; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"ids/nanoid"/utf8>>, function => <<"generate"/utf8>>, line => 50}) end, Alphabet_length = gleam@string:length(Alphabet_string), Size = 21, _assert_subject@1 = check_nanoid_args(Size, Alphabet), {ok, true} = case _assert_subject@1 of {ok, true} -> _assert_subject@1; _assert_fail@1 -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@1, module => <<"ids/nanoid"/utf8>>, function => <<"generate"/utf8>>, line => 56}) end, Mask = calculate_mask(Alphabet_length), Step = calculate_step(Mask, Size, Alphabet_length), _assert_subject@2 = do_generate(Size, Alphabet, Mask, Step, <<""/utf8>>), {ok, Bitstr_nanoid} = case _assert_subject@2 of {ok, _} -> _assert_subject@2; _assert_fail@2 -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@2, module => <<"ids/nanoid"/utf8>>, function => <<"generate"/utf8>>, line => 62}) end, _assert_subject@3 = gleam@bit_string:to_string(Bitstr_nanoid), {ok, Str_nanoid} = case _assert_subject@3 of {ok, _} -> _assert_subject@3; _assert_fail@3 -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@3, module => <<"ids/nanoid"/utf8>>, function => <<"generate"/utf8>>, line => 65}) end, Str_nanoid.