Packages

Fractional indexing library for Gleam

Current section

Files

Jump to
fractional_indexing src fractional_indexing.erl
Raw

src/fractional_indexing.erl

-module(fractional_indexing).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/fractional_indexing.gleam").
-export([increment_integer/2, decrement_integer/2, midpoint/3, generate_key_between/3, 'after'/1, before/1, between/2, first/0, n_between/3, n_first/1, n_after/2, n_before/2]).
-export_type([key_error/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.
?MODULEDOC(
" This library implements fractional indexing outlined in these two blog posts:\n"
" * [Real-time Editing of Ordered Sequences](https://www.figma.com/blog/realtime-editing-of-ordered-sequences/)\n"
" * [Implementing Fractional Indexing](https://observablehq.com/@dgreensp/implementing-fractional-indexing)\n"
"\n"
" Fractional indexing is useful in maintaining an ordered sequence of keys where you want to insert keys at\n"
" arbitary locations in the list. The original Figma post above outlined a system used in their product to\n"
" achieve eventual consistency using a decimal index. For example, in a list with keys `[0, 1]` you could add\n"
" a new item between them with index `0.5`.\n"
"\n"
" Indexing using floats runs into precision issues in Javascript and starts to yield very long keys. This library\n"
" implements a system of variable length integer encoding followed by fractional indexing to keep keys short. Instead of\n"
" sorting items by their numerical index, these keys sort lexicographically using the algorithm at the second link above.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import fractional_indexing as fi\n"
"\n"
" // start with a list of items\n"
" let items = [1, 3]\n"
"\n"
" // create some keys for them, here we will use the bulk API\n"
" // to generate several keys at once and zip them up\n"
" let keys = fi.n_first(num_keys: items |> list.length)\n"
"\n"
" let zipped = list.zip(keys, items)\n"
" // [#(\"a0\", 1), #(\"a1\", 3)]\n"
"\n"
" // now you want to add an item between these two values, so\n"
" // generate a key to index between `a0` and `a1`. Note\n"
" // that key generation can fail in exotic situations, but under\n"
" // normal use should return a suitable key\n"
" let assert [key1, key2] = keys // a0, a1\n"
" let assert Ok(key_between) = fi.between(key1, key2)\n"
" let zipped = [#(key_between, 2), ..zipped]\n"
"\n"
" // sort them to get them in the order you want using string comparison\n"
" // for the keys\n"
" echo zipped |> list.sort(by: fn(a, b) { string.compare(a.0, b.0) })\n"
" // [#(\"a0\", 1), #(\"a0V\", 2) #(\"a1\", 3)]\n"
"\n"
" ```\n"
).
-type key_error() :: err_wrong_order | err_invalid.
-file("src/fractional_indexing.gleam", 369).
-spec get_codepoint(binary()) -> integer().
get_codepoint(S) ->
Codepoint@1 = case begin
_pipe = S,
_pipe@1 = gleam@string:to_utf_codepoints(_pipe),
gleam@list:first(_pipe@1)
end of
{ok, Codepoint} -> Codepoint;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"fractional_indexing"/utf8>>,
function => <<"get_codepoint"/utf8>>,
line => 370,
value => _assert_fail,
start => 12031,
'end' => 12101,
pattern_start => 12042,
pattern_end => 12055})
end,
_pipe@2 = Codepoint@1,
gleam_stdlib:identity(_pipe@2).
-file("src/fractional_indexing.gleam", 374).
-spec codepoint_to_string(integer()) -> binary().
codepoint_to_string(I) ->
Codepoint@1 = case gleam@string:utf_codepoint(I) of
{ok, Codepoint} -> Codepoint;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"fractional_indexing"/utf8>>,
function => <<"codepoint_to_string"/utf8>>,
line => 375,
value => _assert_fail,
start => 12193,
'end' => 12243,
pattern_start => 12204,
pattern_end => 12217})
end,
gleam_stdlib:utf_codepoint_list_to_string([Codepoint@1]).
-file("src/fractional_indexing.gleam", 453).
-spec remove_common_prefix(binary(), binary()) -> {gleam@option:option(binary()),
binary(),
binary()}.
remove_common_prefix(A, B) ->
Prefix = begin
_pipe@3 = gleam@list:zip(
begin
_pipe@1 = gleam@string:pad_end(
A,
begin
_pipe = B,
string:length(_pipe)
end,
<<"0"/utf8>>
),
gleam@string:split(_pipe@1, <<""/utf8>>)
end,
begin
_pipe@2 = B,
gleam@string:split(_pipe@2, <<""/utf8>>)
end
),
gleam@list:fold_until(
_pipe@3,
<<""/utf8>>,
fun(Acc, P) -> case {erlang:element(1, P), erlang:element(2, P)} of
{A@1, B@1} when A@1 =:= B@1 ->
{continue, <<Acc/binary, A@1/binary>>};
{_, _} ->
{stop, Acc}
end end
)
end,
case Prefix of
<<""/utf8>> ->
{none, A, B};
Prefix@1 ->
Len = begin
_pipe@4 = Prefix@1,
string:length(_pipe@4)
end,
A_rem = gleam@string:drop_start(A, Len),
B_rem = gleam@string:drop_start(B, Len),
{{some, Prefix@1}, A_rem, B_rem}
end.
-file("src/fractional_indexing.gleam", 527).
-spec order_ok(binary(), binary()) -> {ok, nil} | {error, key_error()}.
order_ok(A, B) ->
Ok = gleam@string:compare(A, B) =:= lt,
_pipe = Ok,
gleam@bool:lazy_guard(
_pipe,
fun() -> {ok, nil} end,
fun() -> {error, err_wrong_order} end
).
-file("src/fractional_indexing.gleam", 536).
-spec encoding_ok(binary(), binary()) -> {ok, nil} | {error, key_error()}.
encoding_ok(S, Encoding) ->
_pipe = S,
_pipe@1 = gleam@string:split(_pipe, <<""/utf8>>),
_pipe@2 = gleam@list:map(
_pipe@1,
fun(Char) -> gleam_stdlib:contains_string(Encoding, Char) end
),
gleam@list:fold_until(_pipe@2, {ok, nil}, fun(_, Res) -> case Res of
false ->
{stop, {error, err_invalid}};
true ->
{continue, {ok, nil}}
end end).
-file("src/fractional_indexing.gleam", 323).
-spec do_decrement_integer(binary(), binary()) -> gleam@option:option(binary()).
do_decrement_integer(I, Encoding) ->
Lut = begin
_pipe@2 = gleam@list:zip(
begin
_pipe = Encoding,
gleam@string:split(_pipe, <<""/utf8>>)
end,
gleam@list:range(
0,
begin
_pipe@1 = Encoding,
string:length(_pipe@1)
end
)
),
maps:from_list(_pipe@2)
end,
Last_encoding_char@1 = case begin
_pipe@3 = Encoding,
gleam@string:last(_pipe@3)
end of
{ok, Last_encoding_char} -> Last_encoding_char;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"fractional_indexing"/utf8>>,
function => <<"do_decrement_integer"/utf8>>,
line => 331,
value => _assert_fail,
start => 10795,
'end' => 10854,
pattern_start => 10806,
pattern_end => 10828})
end,
{Head@1, Digits@1} = case begin
_pipe@4 = I,
gleam@string:split(_pipe@4, <<""/utf8>>)
end of
[Head | Digits] -> {Head, Digits};
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"fractional_indexing"/utf8>>,
function => <<"do_decrement_integer"/utf8>>,
line => 333,
value => _assert_fail@1,
start => 10858,
'end' => 10909,
pattern_start => 10869,
pattern_end => 10885})
end,
{Borrow@1, Updated_reversed_digits} = begin
_pipe@5 = Digits@1,
_pipe@6 = lists:reverse(_pipe@5),
gleam@list:map_fold(
_pipe@6,
true,
fun(Borrow, C) ->
C_index@1 = case begin
_pipe@7 = Lut,
gleam_stdlib:map_get(_pipe@7, C)
end of
{ok, C_index} -> C_index;
_assert_fail@2 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"fractional_indexing"/utf8>>,
function => <<"do_decrement_integer"/utf8>>,
line => 338,
value => _assert_fail@2,
start => 11033,
'end' => 11076,
pattern_start => 11044,
pattern_end => 11055})
end,
C_next = C_index@1 - 1,
case {Borrow, C_next} of
{true, Index} when Index =:= -1 ->
{true, Last_encoding_char@1};
{true, Index@1} ->
{false,
begin
_pipe@8 = Encoding,
gleam@string:slice(_pipe@8, Index@1, 1)
end};
{false, _} ->
{false, C}
end
end
)
end,
New_digits = begin
_pipe@9 = Updated_reversed_digits,
_pipe@10 = lists:reverse(_pipe@9),
gleam@string:join(_pipe@10, <<""/utf8>>)
end,
case {Borrow@1, Head@1} of
{true, <<"a"/utf8>>} ->
{some, <<"Z"/utf8, Last_encoding_char@1/binary>>};
{true, <<"A"/utf8>>} ->
none;
{true, Head@2} ->
Next_codepoint = get_codepoint(Head@2) - 1,
Next_head = codepoint_to_string(Next_codepoint),
case Next_codepoint of
Cp when Cp < 90 ->
{some,
<<<<Next_head/binary, New_digits/binary>>/binary,
Last_encoding_char@1/binary>>};
_ ->
Pop_digits = begin
_pipe@11 = New_digits,
gleam@string:drop_end(_pipe@11, 1)
end,
{some, <<Next_head/binary, Pop_digits/binary>>}
end;
{false, Head@3} ->
{some, <<Head@3/binary, New_digits/binary>>}
end.
-file("src/fractional_indexing.gleam", 277).
-spec do_increment_integer(binary(), binary()) -> gleam@option:option(binary()).
do_increment_integer(I, Encoding) ->
Lut = begin
_pipe@2 = gleam@list:zip(
begin
_pipe = Encoding,
gleam@string:split(_pipe, <<""/utf8>>)
end,
gleam@list:range(
0,
begin
_pipe@1 = Encoding,
string:length(_pipe@1)
end
)
),
maps:from_list(_pipe@2)
end,
Encoding_length = begin
_pipe@3 = Encoding,
string:length(_pipe@3)
end,
{Head@1, Digits@1} = case begin
_pipe@4 = I,
gleam@string:split(_pipe@4, <<""/utf8>>)
end of
[Head | Digits] -> {Head, Digits};
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"fractional_indexing"/utf8>>,
function => <<"do_increment_integer"/utf8>>,
line => 287,
value => _assert_fail,
start => 9442,
'end' => 9493,
pattern_start => 9453,
pattern_end => 9469})
end,
{Carry@1, Updated_reversed_digits} = begin
_pipe@5 = Digits@1,
_pipe@6 = lists:reverse(_pipe@5),
gleam@list:map_fold(
_pipe@6,
true,
fun(Carry, C) ->
C_index@1 = case begin
_pipe@7 = Lut,
gleam_stdlib:map_get(_pipe@7, C)
end of
{ok, C_index} -> C_index;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"fractional_indexing"/utf8>>,
function => <<"do_increment_integer"/utf8>>,
line => 292,
value => _assert_fail@1,
start => 9615,
'end' => 9658,
pattern_start => 9626,
pattern_end => 9637})
end,
C_next = C_index@1 + 1,
case {Carry, C_next} of
{true, Index} when Index =:= Encoding_length ->
{true, <<"0"/utf8>>};
{true, Index@1} ->
{false,
begin
_pipe@8 = Encoding,
gleam@string:slice(_pipe@8, Index@1, 1)
end};
{false, _} ->
{false, C}
end
end
)
end,
New_digits = begin
_pipe@9 = Updated_reversed_digits,
_pipe@10 = lists:reverse(_pipe@9),
gleam@string:join(_pipe@10, <<""/utf8>>)
end,
case {Carry@1, Head@1} of
{true, <<"Z"/utf8>>} ->
{some, <<"a0"/utf8>>};
{true, <<"z"/utf8>>} ->
none;
{true, Head@2} ->
Next_codepoint = get_codepoint(Head@2) + 1,
Next_head = codepoint_to_string(Next_codepoint),
case Next_codepoint of
Cp when Cp > 97 ->
{some,
<<<<Next_head/binary, New_digits/binary>>/binary,
"0"/utf8>>};
_ ->
Pop_digits = begin
_pipe@11 = New_digits,
gleam@string:drop_end(_pipe@11, 1)
end,
{some, <<Next_head/binary, Pop_digits/binary>>}
end;
{false, Head@3} ->
{some, <<Head@3/binary, New_digits/binary>>}
end.
-file("src/fractional_indexing.gleam", 398).
-spec get_integer_length(gleam@option:option(binary())) -> gleam@option:option(integer()).
get_integer_length(First) ->
case First of
none ->
none;
{some, First@1} ->
case get_codepoint(First@1) of
C when (C >= 65) andalso (C =< 90) ->
{some, (90 - C) + 2};
C@1 when (C@1 >= 97) andalso (C@1 =< 122) ->
{some, (C@1 - 97) + 2};
_ ->
none
end
end.
-file("src/fractional_indexing.gleam", 388).
-spec get_integer_part(binary()) -> gleam@option:option(binary()).
get_integer_part(Key) ->
First = begin
_pipe = Key,
_pipe@1 = gleam@string:first(_pipe),
_pipe@2 = gleam@result:unwrap(_pipe@1, <<""/utf8>>),
gleam@string:to_option(_pipe@2)
end,
Key_length = begin
_pipe@3 = Key,
string:length(_pipe@3)
end,
case get_integer_length(First) of
{some, Len} when Len =< Key_length ->
{some,
begin
_pipe@4 = Key,
gleam@string:slice(_pipe@4, 0, Len)
end};
_ ->
none
end.
-file("src/fractional_indexing.gleam", 414).
-spec validate_integer_ok(binary()) -> gleam@option:option(binary()).
validate_integer_ok(I) ->
Expected_length = begin
_pipe = I,
_pipe@1 = gleam@string:first(_pipe),
_pipe@2 = gleam@result:unwrap(_pipe@1, <<""/utf8>>),
_pipe@3 = gleam@string:to_option(_pipe@2),
_pipe@4 = get_integer_length(_pipe@3),
gleam@option:unwrap(_pipe@4, -1)
end,
Ok = Expected_length =:= begin
_pipe@5 = I,
string:length(_pipe@5)
end,
_pipe@6 = Ok,
gleam@bool:lazy_guard(_pipe@6, fun() -> {some, I} end, fun() -> none end).
-file("src/fractional_indexing.gleam", 265).
?DOC(false).
-spec increment_integer(binary(), binary()) -> gleam@option:option(binary()).
increment_integer(I, Encoding) ->
_pipe = validate_integer_ok(I),
gleam@option:then(
_pipe,
fun(I@1) -> do_increment_integer(I@1, Encoding) end
).
-file("src/fractional_indexing.gleam", 271).
?DOC(false).
-spec decrement_integer(binary(), binary()) -> gleam@option:option(binary()).
decrement_integer(I, Encoding) ->
_pipe = validate_integer_ok(I),
gleam@option:then(
_pipe,
fun(I@1) -> do_decrement_integer(I@1, Encoding) end
).
-file("src/fractional_indexing.gleam", 549).
-spec order_key_ok(binary()) -> {ok, nil} | {error, key_error()}.
order_key_ok(Key) ->
Int_part = get_integer_part(Key),
Fractional_part = case Int_part of
{some, Int_key} ->
gleam@string:drop_start(
Key,
begin
_pipe = Int_key,
string:length(_pipe)
end
);
none ->
<<"0"/utf8>>
end,
Is_smallest_integer = Key =:= <<"A00000000000000000000000000"/utf8>>,
Fractional_ends_in_zero = case Fractional_part of
<<""/utf8>> ->
false;
Part ->
begin
_pipe@1 = Part,
_pipe@2 = gleam@string:last(_pipe@1),
gleam@result:unwrap(_pipe@2, <<"0"/utf8>>)
end
=:= <<"0"/utf8>>
end,
All_ok = not Is_smallest_integer andalso not Fractional_ends_in_zero,
_pipe@3 = All_ok,
gleam@bool:lazy_guard(
_pipe@3,
fun() -> {ok, nil} end,
fun() -> {error, err_invalid} end
).
-file("src/fractional_indexing.gleam", 481).
-spec get_midpoint_between(binary(), binary(), binary()) -> binary().
get_midpoint_between(A, B, Encoding) ->
Lut = begin
_pipe@2 = gleam@list:zip(
begin
_pipe = Encoding,
gleam@string:split(_pipe, <<""/utf8>>)
end,
gleam@list:range(
0,
begin
_pipe@1 = Encoding,
string:length(_pipe@1)
end
)
),
maps:from_list(_pipe@2)
end,
A_first@1 = case begin
_pipe@3 = A,
gleam@string:first(_pipe@3)
end of
{ok, A_first} -> A_first;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"fractional_indexing"/utf8>>,
function => <<"get_midpoint_between"/utf8>>,
line => 490,
value => _assert_fail,
start => 15537,
'end' => 15579,
pattern_start => 15548,
pattern_end => 15559})
end,
Index_a@1 = case begin
_pipe@4 = Lut,
gleam_stdlib:map_get(_pipe@4, A_first@1)
end of
{ok, Index_a} -> Index_a;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"fractional_indexing"/utf8>>,
function => <<"get_midpoint_between"/utf8>>,
line => 491,
value => _assert_fail@1,
start => 15582,
'end' => 15631,
pattern_start => 15593,
pattern_end => 15604})
end,
B_first = begin
_pipe@5 = B,
_pipe@6 = gleam@string:first(_pipe@5),
gleam@result:unwrap(_pipe@6, <<""/utf8>>)
end,
Index_b = begin
_pipe@7 = Lut,
_pipe@8 = gleam_stdlib:map_get(_pipe@7, B_first),
gleam@result:unwrap(
_pipe@8,
begin
_pipe@9 = Encoding,
string:length(_pipe@9)
end
)
end,
Index_midpt@1 = case begin
_pipe@10 = Index_a@1,
_pipe@11 = gleam@int:add(_pipe@10, Index_b),
gleam@int:divide(_pipe@11, 2)
end of
{ok, Index_midpt} -> Index_midpt;
_assert_fail@2 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"fractional_indexing"/utf8>>,
function => <<"get_midpoint_between"/utf8>>,
line => 500,
value => _assert_fail@2,
start => 15923,
'end' => 15996,
pattern_start => 15934,
pattern_end => 15949})
end,
case {Index_a@1, Index_b, Index_midpt@1} of
{Index_a@2, _, Index_midpt@2} when Index_midpt@2 > Index_a@2 ->
gleam@string:slice(Encoding, Index_midpt@2, 1);
{_, _, _} ->
case begin
_pipe@12 = B,
string:length(_pipe@12)
end of
0 ->
A_rem = begin
_pipe@13 = A,
_pipe@14 = gleam@string:drop_start(_pipe@13, 1),
gleam@string:to_option(_pipe@14)
end,
<<A_first@1/binary,
(midpoint(A_rem, none, Encoding))/binary>>;
1 ->
A_rem = begin
_pipe@13 = A,
_pipe@14 = gleam@string:drop_start(_pipe@13, 1),
gleam@string:to_option(_pipe@14)
end,
<<A_first@1/binary,
(midpoint(A_rem, none, Encoding))/binary>>;
_ ->
B_first
end
end.
-file("src/fractional_indexing.gleam", 434).
?DOC(false).
-spec midpoint(
gleam@option:option(binary()),
gleam@option:option(binary()),
binary()
) -> binary().
midpoint(A, B, Encoding) ->
A@1 = begin
_pipe = A,
gleam@option:unwrap(_pipe, <<"0"/utf8>>)
end,
B@1 = begin
_pipe@1 = B,
gleam@option:unwrap(_pipe@1, <<""/utf8>>)
end,
{Prefix, A@2, B@2} = remove_common_prefix(A@1, B@1),
case Prefix of
none ->
get_midpoint_between(A@2, B@2, Encoding);
{some, Prefix@1} ->
<<Prefix@1/binary,
(midpoint(
begin
_pipe@2 = A@2,
gleam@string:to_option(_pipe@2)
end,
begin
_pipe@3 = B@2,
gleam@string:to_option(_pipe@3)
end,
Encoding
))/binary>>
end.
-file("src/fractional_indexing.gleam", 192).
-spec do_generate_key_between(
gleam@option:option(binary()),
gleam@option:option(binary()),
binary()
) -> gleam@option:option(binary()).
do_generate_key_between(A, B, Encoding) ->
case {A, B} of
{none, none} ->
{some, <<"a0"/utf8>>};
{none, {some, B@1}} ->
Int_part@1 = case get_integer_part(B@1) of
{some, Int_part} -> Int_part;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"fractional_indexing"/utf8>>,
function => <<"do_generate_key_between"/utf8>>,
line => 200,
value => _assert_fail,
start => 6693,
'end' => 6740,
pattern_start => 6704,
pattern_end => 6718})
end,
Frac_part = begin
_pipe = B@1,
gleam@string:drop_start(
_pipe,
begin
_pipe@1 = Int_part@1,
string:length(_pipe@1)
end
)
end,
case Int_part@1 of
Ip when Ip =:= <<"A00000000000000000000000000"/utf8>> ->
{some,
<<Ip/binary,
(midpoint(
none,
begin
_pipe@2 = Frac_part,
gleam@string:to_option(_pipe@2)
end,
Encoding
))/binary>>};
Ip@1 ->
case gleam@string:compare(Ip@1, B@1) =:= lt of
true ->
{some, Ip@1};
_ ->
decrement_integer(Ip@1, Encoding)
end
end;
{{some, A@1}, none} ->
Int_part@3 = case get_integer_part(A@1) of
{some, Int_part@2} -> Int_part@2;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"fractional_indexing"/utf8>>,
function => <<"do_generate_key_between"/utf8>>,
line => 214,
value => _assert_fail@1,
start => 7163,
'end' => 7210,
pattern_start => 7174,
pattern_end => 7188})
end,
Frac_part@1 = begin
_pipe@3 = A@1,
gleam@string:drop_start(
_pipe@3,
begin
_pipe@4 = Int_part@3,
string:length(_pipe@4)
end
)
end,
case increment_integer(Int_part@3, Encoding) of
{some, Next} ->
{some, Next};
none ->
{some,
<<Int_part@3/binary,
(midpoint(
begin
_pipe@5 = Frac_part@1,
gleam@string:to_option(_pipe@5)
end,
none,
Encoding
))/binary>>}
end;
{{some, A@2}, {some, B@2}} ->
Int_part_a@1 = case get_integer_part(A@2) of
{some, Int_part_a} -> Int_part_a;
_assert_fail@2 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"fractional_indexing"/utf8>>,
function => <<"do_generate_key_between"/utf8>>,
line => 225,
value => _assert_fail@2,
start => 7538,
'end' => 7587,
pattern_start => 7549,
pattern_end => 7565})
end,
Frac_part_a = begin
_pipe@6 = A@2,
gleam@string:drop_start(
_pipe@6,
begin
_pipe@7 = Int_part_a@1,
string:length(_pipe@7)
end
)
end,
Int_part_b@1 = case get_integer_part(B@2) of
{some, Int_part_b} -> Int_part_b;
_assert_fail@3 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"fractional_indexing"/utf8>>,
function => <<"do_generate_key_between"/utf8>>,
line => 227,
value => _assert_fail@3,
start => 7670,
'end' => 7719,
pattern_start => 7681,
pattern_end => 7697})
end,
Frac_part_b = begin
_pipe@8 = B@2,
gleam@string:drop_start(
_pipe@8,
begin
_pipe@9 = Int_part_b@1,
string:length(_pipe@9)
end
)
end,
case Int_part_a@1 =:= Int_part_b@1 of
true ->
{some,
<<Int_part_a@1/binary,
(midpoint(
begin
_pipe@10 = Frac_part_a,
gleam@string:to_option(_pipe@10)
end,
begin
_pipe@11 = Frac_part_b,
gleam@string:to_option(_pipe@11)
end,
Encoding
))/binary>>};
false ->
Next_int_a = increment_integer(Int_part_a@1, Encoding),
case Next_int_a of
{some, Ia} ->
case gleam@string:compare(Ia, B@2) =:= lt of
true ->
Next_int_a;
false ->
{some,
<<Int_part_a@1/binary,
(midpoint(
begin
_pipe@12 = Frac_part_a,
gleam@string:to_option(
_pipe@12
)
end,
none,
Encoding
))/binary>>}
end;
none ->
none
end
end
end.
-file("src/fractional_indexing.gleam", 162).
?DOC(false).
-spec generate_key_between(
gleam@option:option(binary()),
gleam@option:option(binary()),
binary()
) -> {ok, binary()} | {error, key_error()}.
generate_key_between(A, B, Encoding) ->
Identity = fun(V) -> V end,
Check_ok = case {A, B} of
{{some, A@1}, {some, B@1}} ->
_pipe = [order_ok(A@1, B@1),
order_key_ok(A@1),
order_key_ok(B@1),
encoding_ok(<<A@1/binary, B@1/binary>>, Encoding)],
gleam@list:try_each(_pipe, Identity);
{{some, A@2}, none} ->
_pipe@1 = [order_key_ok(A@2), encoding_ok(A@2, Encoding)],
gleam@list:try_each(_pipe@1, Identity);
{none, {some, B@2}} ->
_pipe@2 = [order_key_ok(B@2), encoding_ok(B@2, Encoding)],
gleam@list:try_each(_pipe@2, Identity);
{none, none} ->
{ok, nil}
end,
case {Check_ok, A, B} of
{{error, E}, _, _} ->
{error, E};
{{ok, nil}, A@3, B@3} ->
_pipe@3 = do_generate_key_between(A@3, B@3, Encoding),
gleam@option:to_result(_pipe@3, err_invalid)
end.
-file("src/fractional_indexing.gleam", 73).
?DOC(" Generate a key that will sort later than the given key\n").
-spec 'after'(binary()) -> {ok, binary()} | {error, key_error()}.
'after'(Key) ->
generate_key_between(
begin
_pipe = Key,
gleam@string:to_option(_pipe)
end,
none,
<<"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"/utf8>>
).
-file("src/fractional_indexing.gleam", 79).
?DOC(" Generate a key that will sort before the given key\n").
-spec before(binary()) -> {ok, binary()} | {error, key_error()}.
before(Key) ->
generate_key_between(
none,
begin
_pipe = Key,
gleam@string:to_option(_pipe)
end,
<<"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"/utf8>>
).
-file("src/fractional_indexing.gleam", 85).
?DOC(" Generate a key that will sort between key1 and key2\n").
-spec between(binary(), binary()) -> {ok, binary()} | {error, key_error()}.
between(Key1, Key2) ->
generate_key_between(
begin
_pipe = Key1,
gleam@string:to_option(_pipe)
end,
begin
_pipe@1 = Key2,
gleam@string:to_option(_pipe@1)
end,
<<"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"/utf8>>
).
-file("src/fractional_indexing.gleam", 96).
?DOC(
" Return the first key, defined as the zero-key `a0`. This is useful when assigning a key to the first element of a list. Note that you cannot start a list of keys from any arbitrary string because the encoding\n"
" of the key matters.\n"
).
-spec first() -> binary().
first() ->
First@1 = case generate_key_between(
none,
none,
<<"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"/utf8>>
) of
{ok, First} -> First;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"fractional_indexing"/utf8>>,
function => <<"first"/utf8>>,
line => 97,
value => _assert_fail,
start => 3948,
'end' => 4011,
pattern_start => 3959,
pattern_end => 3968})
end,
First@1.
-file("src/fractional_indexing.gleam", 142).
?DOC(
" Generate N keys between key1 and key 2\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import fractional_indexing as fi\n"
"\n"
" // start with a list of some items which we want to index between existing keys `a0` and `a1`\n"
" let items = [1, 2, 3]\n"
"\n"
" // generate some keys\n"
" let assert Ok(keys) = fi.n_between(\"a0\", \"a1\")\n"
"\n"
" // do something with the keys\n"
" items\n"
" |> list.map2(keys, fn(item, key) {\n"
" #(key, item)\n"
" })\n"
" // output: [#(\"a0V\", 1), #(\"a0k\", 2), #(\"a0s\", 3)]\n"
" ```\n"
).
-spec n_between(binary(), binary(), integer()) -> {ok, list(binary())} |
{error, key_error()}.
n_between(Key1, Key2, N) ->
_pipe = gleam@list:range(0, N - 1),
gleam@list:try_fold(_pipe, [], fun(Res, _) -> case Res of
[] ->
_pipe@1 = between(Key1, Key2),
gleam@result:map(_pipe@1, fun(Key) -> [Key] end);
A ->
Last@1 = case begin
_pipe@2 = A,
gleam@list:last(_pipe@2)
end of
{ok, Last} -> Last;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"fractional_indexing"/utf8>>,
function => <<"n_between"/utf8>>,
line => 152,
value => _assert_fail,
start => 5327,
'end' => 5363,
pattern_start => 5338,
pattern_end => 5346})
end,
_pipe@3 = between(Last@1, Key2),
gleam@result:map(
_pipe@3,
fun(Key@1) -> lists:append(A, [Key@1]) end
)
end end).
-file("src/fractional_indexing.gleam", 103).
?DOC(" Generate N consecutive keys starting from the zero key `a0`\n").
-spec n_first(integer()) -> list(binary()).
n_first(N) ->
Start@1 = case before(first()) of
{ok, Start} -> Start;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"fractional_indexing"/utf8>>,
function => <<"n_first"/utf8>>,
line => 104,
value => _assert_fail,
start => 4143,
'end' => 4181,
pattern_start => 4154,
pattern_end => 4163})
end,
N_first@1 = case n_between(Start@1, <<""/utf8>>, N) of
{ok, N_first} -> N_first;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"fractional_indexing"/utf8>>,
function => <<"n_first"/utf8>>,
line => 105,
value => _assert_fail@1,
start => 4184,
'end' => 4232,
pattern_start => 4195,
pattern_end => 4206})
end,
N_first@1.
-file("src/fractional_indexing.gleam", 111).
?DOC(" Generate N keys after the given key\n").
-spec n_after(binary(), integer()) -> {ok, list(binary())} |
{error, key_error()}.
n_after(Key, N) ->
n_between(Key, <<""/utf8>>, N).
-file("src/fractional_indexing.gleam", 117).
?DOC(" Generate N keys before the given key\n").
-spec n_before(binary(), integer()) -> {ok, list(binary())} |
{error, key_error()}.
n_before(Key, N) ->
n_between(<<""/utf8>>, Key, N).