-module(ids@cuid). -compile(no_auto_import). -export([start/0, gen/1, is_cuid/1, slug/1, is_slug/1]). -export_type([message/0, state/0, char_list/0]). -opaque message() :: {generate, gleam@otp@process:sender(binary())} | {generate_slug, gleam@otp@process:sender(binary())}. -opaque state() :: {state, integer(), binary()}. -type char_list() :: any(). -spec start() -> {ok, gleam@otp@process:sender(message())} | {error, gleam@otp@actor:start_error()}. start() -> gleam@otp@actor:start({state, 0, get_fingerprint()}, fun handle_msg/2). -spec gen(gleam@otp@process:sender(message())) -> binary(). gen(Channel) -> gleam@otp@actor:call(Channel, fun(A) -> {generate, A} end, 1000). -spec is_cuid(binary()) -> boolean(). is_cuid(Id) -> gleam@string:starts_with(Id, <<"c"/utf8>>). -spec slug(gleam@otp@process:sender(message())) -> binary(). slug(Channel) -> gleam@otp@actor:call(Channel, fun(A) -> {generate_slug, A} end, 1000). -spec is_slug(binary()) -> boolean(). is_slug(Slug) -> Slug_length = gleam@string:length(Slug), (Slug_length >= 7) andalso (Slug_length =< 10). -spec handle_msg(message(), state()) -> gleam@otp@actor:next(state()). handle_msg(Msg, State) -> case Msg of {generate, Reply} -> Id = format_id( [<<"c"/utf8>>, timestamp(), format_count(erlang:element(2, State)), erlang:element(3, State), random_block(), random_block()] ), gleam@otp@actor:send(Reply, Id), {continue, erlang:setelement(2, State, new_count(erlang:element(2, State)))}; {generate_slug, Reply@1} -> Slug = format_id( [gleam@string:slice(timestamp(), -2, 2), gleam@string:slice( format_count(erlang:element(2, State)), -4, 4 ), gleam@string:concat( [gleam@string:slice(erlang:element(3, State), 0, 1), gleam@string:slice(erlang:element(3, State), -1, 1)] ), gleam@string:slice(random_block(), -2, 2)] ), gleam@otp@actor:send(Reply@1, Slug), {continue, erlang:setelement(2, State, new_count(erlang:element(2, State)))} end. -spec format_id(list(binary())) -> binary(). format_id(Id_data) -> gleam@string:lowercase(gleam@string:concat(Id_data)). -spec new_count(integer()) -> integer(). new_count(Count) -> case Count < 1679616 of true -> Count + 1; false -> 0 end. -spec timestamp() -> binary(). timestamp() -> Secs = gleam@os:system_time(millisecond), gleam@int:to_base_string(Secs, 36). -spec format_count(integer()) -> binary(). format_count(Num) -> gleam@string:pad_left(gleam@int:to_base_string(Num, 36), 4, <<"0"/utf8>>). -spec get_fingerprint() -> binary(). get_fingerprint() -> Operator = 36 * 36, {ok, Pid} = gleam@int:parse(erlang:list_to_binary(os:getpid())), Id = (case Operator of 0 -> 0; Gleam@denominator -> Pid rem Gleam@denominator end) * Operator, Localhost = net_adm:localhost(), Sum = gleam@list:fold(Localhost, 0, fun(Char, Acc) -> Char + Acc end), Hostid = case Operator of 0 -> 0; Gleam@denominator@1 -> ((Sum + gleam@list:length(Localhost)) + 36) rem Gleam@denominator@1 end, gleam@int:to_base_string(Id + Hostid, 36). -spec random_block() -> binary(). random_block() -> gleam@string:pad_left( gleam@int:to_base_string(rand:uniform(1679616 - 1), 36), 4, <<"0"/utf8>> ).