Current section

Files

Jump to
ids src ids@snowflake.erl
Raw

src/ids@snowflake.erl

-module(ids@snowflake).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([generate/1, decode/1, start_with_epoch/2, start/1]).
-export_type([message/0, state/0]).
-opaque message() :: {generate, gleam@erlang@process:subject(integer())}.
-opaque state() :: {state, integer(), integer(), integer(), integer()}.
-spec generate(gleam@erlang@process:subject(message())) -> integer().
generate(Channel) ->
gleam@otp@actor:call(Channel, fun(Field@0) -> {generate, Field@0} end, 1000).
-spec decode(integer()) -> {ok, {integer(), integer(), integer()}} |
{error, binary()}.
decode(Snowflake) ->
case binary:encode_unsigned(Snowflake) of
<<Timestamp:42/integer, Machine_id:10/integer, Idx:12/integer>> ->
{ok, {Timestamp, Machine_id, Idx}};
_ ->
{error, <<"Error: Couldn't decode snowflake id."/utf8>>}
end.
-spec update_state(state()) -> state().
update_state(State) ->
Now = begin
_pipe = os:system_time(millisecond),
gleam@int:subtract(_pipe, erlang:element(2, State))
end,
case erlang:element(3, State) of
Lt when (Lt =:= Now) andalso (erlang:element(5, State) < 4095) ->
erlang:setelement(5, State, erlang:element(5, State) + 1);
Lt@1 when Lt@1 =:= Now ->
update_state(State);
_ ->
erlang:setelement(3, State, Now)
end.
-spec handle_msg(message(), state()) -> gleam@otp@actor:next(message(), state()).
handle_msg(Msg, State) ->
case Msg of
{generate, Reply} ->
New_state = update_state(State),
Snowflake = begin
_pipe = erlang:element(3, New_state),
_pipe@1 = erlang:'bsl'(_pipe, 22),
erlang:'bor'(
_pipe@1,
begin
_pipe@2 = erlang:element(4, New_state),
_pipe@3 = erlang:'bsl'(_pipe@2, 12),
erlang:'bor'(_pipe@3, erlang:element(5, New_state))
end
)
end,
gleam@otp@actor:send(Reply, Snowflake),
gleam@otp@actor:continue(New_state)
end.
-spec start_with_epoch(integer(), integer()) -> {ok,
gleam@erlang@process:subject(message())} |
{error, binary()}.
start_with_epoch(Machine_id, Epoch) ->
case Epoch > os:system_time(millisecond) of
true ->
{error, <<"Error: Epoch can't be larger than current time."/utf8>>};
false ->
_pipe = {state, Epoch, 0, Machine_id, 0},
_pipe@1 = gleam@otp@actor:start(_pipe, fun handle_msg/2),
gleam@result:map_error(
_pipe@1,
fun(Err) ->
<<"Error: Couldn't start actor. Reason: "/utf8,
(gleam@string:inspect(Err))/binary>>
end
)
end.
-spec start(integer()) -> {ok, gleam@erlang@process:subject(message())} |
{error, binary()}.
start(Machine_id) ->
start_with_epoch(Machine_id, 0).