Current section
Files
Jump to
Current section
Files
src/cuid2_gleam.erl
-module(cuid2_gleam).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([new/0, with_length/2, with_counter/2, with_randomiser/2, with_fingerprint/2, is_cuid_like/1, is_valid/2, create/1, build/1, default/0]).
-export_type([default_counter/0, custom_counter/0, default_length/0, custom_length/0, default_fingerprint/0, custom_fingerprint/0, default_randomiser/0, custom_randomiser/0, generator/0, builder/4]).
-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 default_counter() :: any().
-type custom_counter() :: any().
-type default_length() :: any().
-type custom_length() :: any().
-type default_fingerprint() :: any().
-type custom_fingerprint() :: any().
-type default_randomiser() :: any().
-type custom_randomiser() :: any().
-opaque generator() :: {generator,
cuid2_gleam@internal@counter:counter(),
fun(() -> float()),
binary(),
integer()}.
-opaque builder(FOB, FOC, FOD, FOE) :: {builder,
gleam@option:option(cuid2_gleam@internal@counter:counter()),
gleam@option:option(fun(() -> float())),
gleam@option:option(binary()),
gleam@option:option(integer())} |
{gleam_phantom, FOB, FOC, FOD, FOE}.
-file("src/cuid2_gleam.gleam", 55).
?DOC(
" Creates a Builder with default options so you can customise them\n"
" as needed\n"
).
-spec new() -> builder(default_counter(), default_randomiser(), default_fingerprint(), default_length()).
new() ->
{builder, none, none, none, none}.
-file("src/cuid2_gleam.gleam", 65).
?DOC(" Will change the length of the created ids (default is 24)\n").
-spec with_length(builder(FOJ, FOK, FOL, default_length()), integer()) -> builder(FOJ, FOK, FOL, custom_length()).
with_length(Builder, Length) ->
_record = Builder,
{builder,
erlang:element(2, _record),
erlang:element(3, _record),
erlang:element(4, _record),
{some, Length}}.
-file("src/cuid2_gleam.gleam", 74).
?DOC(
" Generation uses a counter that is expected to increment on each `create` call.\n"
" You can customise that, I don't fully understand all of it, so do this at your own risks.\n"
).
-spec with_counter(
builder(default_counter(), FOU, FOV, FOW),
cuid2_gleam@internal@counter:counter()
) -> builder(custom_counter(), FOU, FOV, FOW).
with_counter(Builder, Counter) ->
_record = Builder,
{builder,
{some, Counter},
erlang:element(3, _record),
erlang:element(4, _record),
erlang:element(5, _record)}.
-file("src/cuid2_gleam.gleam", 82).
?DOC(" The randomiser used can also be custom if needed\n").
-spec with_randomiser(
builder(FPF, default_randomiser(), FPG, FPH),
fun(() -> float())
) -> builder(FPF, custom_randomiser(), FPG, FPH).
with_randomiser(Builder, Randomiser) ->
_record = Builder,
{builder,
erlang:element(2, _record),
{some, Randomiser},
erlang:element(4, _record),
erlang:element(5, _record)}.
-file("src/cuid2_gleam.gleam", 92).
?DOC(
" The fingerprint is to allow to differentiate even more\n"
" on distributed system, I think… not 100% sure.\n"
" Default is random\n"
).
-spec with_fingerprint(builder(FPQ, FPR, default_fingerprint(), FPS), binary()) -> builder(FPQ, FPR, custom_fingerprint(), FPS).
with_fingerprint(Builder, Fingerprint) ->
_record = Builder,
{builder,
erlang:element(2, _record),
erlang:element(3, _record),
{some, Fingerprint},
erlang:element(5, _record)}.
-file("src/cuid2_gleam.gleam", 138).
?DOC(
" Returns true if the given string looks like a cuid.\n"
" It's just a regex check, so \"thisisacuid\" is true.\n"
).
-spec is_cuid_like(binary()) -> boolean().
is_cuid_like(Maybe_cuid) ->
_assert_subject = gleam@regexp:from_string(<<"^[a-z][0-9a-z]+$"/utf8>>),
{ok, Regex} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
value => _assert_fail,
module => <<"cuid2_gleam"/utf8>>,
function => <<"is_cuid_like"/utf8>>,
line => 139})
end,
gleam@regexp:check(Regex, Maybe_cuid).
-file("src/cuid2_gleam.gleam", 129).
?DOC(" Returns true if the given string has been generated by this generator\n").
-spec is_valid(generator(), binary()) -> boolean().
is_valid(Generator, Maybe_cuid) ->
case erlang:element(5, Generator) =:= string:length(Maybe_cuid) of
true ->
is_cuid_like(Maybe_cuid);
false ->
false
end.
-file("src/cuid2_gleam.gleam", 143).
-spec get_first_letter() -> binary().
get_first_letter() ->
Alphabet = <<"abcdefghijklmnopqrstuvwxyz"/utf8>>,
_assert_subject = begin
_pipe = Alphabet,
_pipe@1 = gleam@string:to_graphemes(_pipe),
_pipe@2 = gleam@list:shuffle(_pipe@1),
gleam@list:first(_pipe@2)
end,
{ok, First} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
value => _assert_fail,
module => <<"cuid2_gleam"/utf8>>,
function => <<"get_first_letter"/utf8>>,
line => 146})
end,
First.
-file("src/cuid2_gleam.gleam", 154).
-spec get_system_time() -> binary().
get_system_time() ->
_pipe = gleam@time@timestamp:system_time(),
_pipe@1 = gleam@time@timestamp:to_unix_seconds(_pipe),
_pipe@2 = erlang:trunc(_pipe@1),
gleam@int:to_base36(_pipe@2).
-file("src/cuid2_gleam.gleam", 169).
-spec bit_array_to_int(bitstring(), integer()) -> integer().
bit_array_to_int(Bit, Value) ->
case Bit of
<<X/integer>> ->
erlang:'bsl'(Value, 8) + X;
<<X@1/integer, Rest/bitstring>> ->
bit_array_to_int(Rest, erlang:'bsl'(Value, 8) + X@1);
_ ->
erlang:error(#{gleam_error => panic,
message => <<"BitArray hash contains something else than int"/utf8>>,
module => <<"cuid2_gleam"/utf8>>,
function => <<"bit_array_to_int"/utf8>>,
line => 175})
end.
-file("src/cuid2_gleam.gleam", 161).
-spec hash(binary()) -> binary().
hash(Input) ->
Hashed_input = gleam@crypto:hash(sha512, gleam_stdlib:identity(Input)),
Int_input = bit_array_to_int(Hashed_input, 0),
_pipe = gleam@int:to_base36(Int_input),
_pipe@1 = gleam@string:drop_start(_pipe, 1),
string:lowercase(_pipe@1).
-file("src/cuid2_gleam.gleam", 183).
-spec do_create_entropy(integer(), fun(() -> float()), binary()) -> binary().
do_create_entropy(Length, Random, Entropy) ->
gleam@bool:guard(
string:length(Entropy) >= Length,
Entropy,
fun() ->
do_create_entropy(
Length,
Random,
<<Entropy/binary,
(gleam@int:to_base36(erlang:trunc(Random() * 36.0)))/binary>>
)
end
).
-file("src/cuid2_gleam.gleam", 179).
-spec create_entropy(integer(), fun(() -> float())) -> binary().
create_entropy(Length, Random) ->
do_create_entropy(Length, Random, <<""/utf8>>).
-file("src/cuid2_gleam.gleam", 119).
?DOC(" Returns a cuid following the Generator configuration.\n").
-spec create(generator()) -> binary().
create(Generator) ->
First_letter = get_first_letter(),
Time = get_system_time(),
Count = gleam@int:to_base36(
cuid2_gleam@internal@counter:tick(erlang:element(2, Generator))
),
Salt = create_entropy(
erlang:element(5, Generator),
erlang:element(3, Generator)
),
Hash_input = <<<<<<Time/binary, Salt/binary>>/binary, Count/binary>>/binary,
(erlang:element(4, Generator))/binary>>,
<<First_letter/binary,
(gleam@string:slice(
hash(Hash_input),
1,
erlang:element(5, Generator) - 1
))/binary>>.
-file("src/cuid2_gleam.gleam", 192).
-spec create_fingerprint(fun(() -> float())) -> binary().
create_fingerprint(Random) ->
_pipe = create_entropy(32, Random),
_pipe@1 = hash(_pipe),
gleam@string:slice(_pipe@1, 0, 32).
-file("src/cuid2_gleam.gleam", 100).
?DOC(" Build a Generator from a Builder!\n").
-spec build(builder(any(), any(), any(), any())) -> generator().
build(Builder) ->
Counter = gleam@option:lazy_unwrap(
erlang:element(2, Builder),
fun cuid2_gleam@internal@counter:new/0
),
Randomiser = gleam@option:unwrap(
erlang:element(3, Builder),
fun rand:uniform/0
),
Fingerprint = gleam@option:lazy_unwrap(
erlang:element(4, Builder),
fun() -> create_fingerprint(Randomiser) end
),
Length = gleam@option:unwrap(erlang:element(5, Builder), 24),
{generator, Counter, Randomiser, Fingerprint, Length}.
-file("src/cuid2_gleam.gleam", 113).
?DOC(
" Returns a new Generator with Default values\n"
" same as `createId` from JS implementation.\n"
).
-spec default() -> generator().
default() ->
_pipe = new(),
build(_pipe).