Current section
Files
Jump to
Current section
Files
src/shelf@set.erl
-module(shelf@set).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/shelf/set.gleam").
-export([open_config/1, open/2, close/1, with_table/3, lookup/2, member/2, to_list/1, fold/3, size/1, save/1, insert/3, insert_list/2, insert_new/3, delete_key/2, delete_object/3, delete_all/1, reload/1, sync/1, update_counter/3]).
-export_type([p_set/2]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-opaque p_set(MID, MIE) :: {p_set,
shelf@internal:ets_ref(),
shelf@internal:dets_ref(),
shelf:write_mode()} |
{gleam_phantom, MID, MIE}.
-file("src/shelf/set.gleam", 47).
?DOC(
" Open a persistent set table with full configuration.\n"
"\n"
" If the DETS file exists, its contents are loaded into a fresh ETS\n"
" table. If no file exists, both tables start empty.\n"
"\n"
" ```gleam\n"
" let config =\n"
" shelf.config(name: \"cache\", path: \"data/cache.dets\")\n"
" |> shelf.write_mode(shelf.WriteThrough)\n"
" let assert Ok(table) = set.open_config(config)\n"
" ```\n"
).
-spec open_config(shelf:config()) -> {ok, p_set(any(), any())} |
{error, shelf:shelf_error()}.
open_config(Config) ->
{config, Name, Path, Write_mode} = Config,
_pipe = shelf_ffi:open_set(Name, Path),
gleam@result:map(
_pipe,
fun(Refs) ->
{p_set,
erlang:element(1, Refs),
erlang:element(2, Refs),
Write_mode}
end
).
-file("src/shelf/set.gleam", 59).
?DOC(
" Open a persistent set table with defaults (WriteBack mode).\n"
"\n"
" ```gleam\n"
" let assert Ok(table) = set.open(\"users\", \"data/users.dets\")\n"
" ```\n"
).
-spec open(binary(), binary()) -> {ok, p_set(any(), any())} |
{error, shelf:shelf_error()}.
open(Name, Path) ->
open_config(shelf:config(Name, Path)).
-file("src/shelf/set.gleam", 71).
?DOC(
" Close the table, saving all data to disk.\n"
"\n"
" Performs a final snapshot of ETS to DETS, closes the DETS file,\n"
" and deletes the ETS table. The handle must not be used after closing.\n"
).
-spec close(p_set(any(), any())) -> {ok, nil} | {error, shelf:shelf_error()}.
close(Table) ->
shelf_ffi:close(erlang:element(2, Table), erlang:element(3, Table)).
-file("src/shelf/set.gleam", 85).
?DOC(
" Use a table within a callback, ensuring it is closed afterward.\n"
"\n"
" The table is opened before the callback and closed after it returns\n"
" (even if it returns an error). Data is auto-saved on close.\n"
"\n"
" ```gleam\n"
" use table <- set.with_table(\"cache\", \"data/cache.dets\")\n"
" set.insert(table, \"key\", \"value\")\n"
" ```\n"
).
-spec with_table(
binary(),
binary(),
fun((p_set(any(), any())) -> {ok, MJB} | {error, shelf:shelf_error()})
) -> {ok, MJB} | {error, shelf:shelf_error()}.
with_table(Name, Path, Fun) ->
gleam@result:'try'(
open(Name, Path),
fun(Table) ->
Result = Fun(Table),
_ = close(Table),
Result
end
).
-file("src/shelf/set.gleam", 105).
?DOC(
" Look up the value for a key.\n"
"\n"
" Reads from ETS — consistent microsecond latency regardless of\n"
" table size or whether the data has been saved to disk.\n"
"\n"
" Returns `Error(NotFound)` if the key does not exist.\n"
).
-spec lookup(p_set(MJG, MJH), MJG) -> {ok, MJH} | {error, shelf:shelf_error()}.
lookup(Table, Key) ->
shelf_ffi:lookup_set(erlang:element(2, Table), Key).
-file("src/shelf/set.gleam", 111).
?DOC(" Check if a key exists without returning the value.\n").
-spec member(p_set(MJM, any()), MJM) -> {ok, boolean()} |
{error, shelf:shelf_error()}.
member(Table, Key) ->
shelf_ffi:member(erlang:element(2, Table), Key).
-file("src/shelf/set.gleam", 119).
?DOC(
" Return all key-value pairs as a list.\n"
"\n"
" **Warning**: loads entire table into memory.\n"
).
-spec to_list(p_set(MJS, MJT)) -> {ok, list({MJS, MJT})} |
{error, shelf:shelf_error()}.
to_list(Table) ->
shelf_ffi:to_list(erlang:element(2, Table)).
-file("src/shelf/set.gleam", 125).
?DOC(" Fold over all entries. Order is unspecified.\n").
-spec fold(p_set(MJZ, MKA), MKD, fun((MKD, MJZ, MKA) -> MKD)) -> {ok, MKD} |
{error, shelf:shelf_error()}.
fold(Table, Initial, Fun) ->
Wrapper = fun(Entry, Acc) ->
Fun(Acc, erlang:element(1, Entry), erlang:element(2, Entry))
end,
shelf_ffi:fold(erlang:element(2, Table), Wrapper, Initial).
-file("src/shelf/set.gleam", 138).
?DOC(" Return the number of entries in the table.\n").
-spec size(p_set(any(), any())) -> {ok, integer()} |
{error, shelf:shelf_error()}.
size(Table) ->
shelf_ffi:size(erlang:element(2, Table)).
-file("src/shelf/set.gleam", 225).
?DOC(
" Snapshot the current ETS contents to DETS.\n"
"\n"
" Uses `ets:to_dets/2` internally — atomically replaces all DETS\n"
" contents with the current ETS state. This is efficient: the\n"
" transfer happens in the Erlang VM without materializing the\n"
" entire table as a list.\n"
"\n"
" ```gleam\n"
" // After a batch of writes...\n"
" let assert Ok(Nil) = set.save(table)\n"
" ```\n"
).
-spec save(p_set(any(), any())) -> {ok, nil} | {error, shelf:shelf_error()}.
save(Table) ->
shelf_ffi:save(erlang:element(2, Table), erlang:element(3, Table)).
-file("src/shelf/set.gleam", 280).
?DOC(" If in WriteThrough mode, save ETS→DETS after every write.\n").
-spec maybe_write_through(p_set(any(), any())) -> {ok, nil} |
{error, shelf:shelf_error()}.
maybe_write_through(Table) ->
case erlang:element(4, Table) of
write_through ->
shelf_ffi:save(erlang:element(2, Table), erlang:element(3, Table));
write_back ->
{ok, nil}
end.
-file("src/shelf/set.gleam", 149).
?DOC(
" Insert a key-value pair. Overwrites if key exists.\n"
"\n"
" In WriteBack mode, only ETS is updated — call `save()` to persist.\n"
" In WriteThrough mode, both ETS and DETS are updated.\n"
).
-spec insert(p_set(MKM, MKN), MKM, MKN) -> {ok, nil} |
{error, shelf:shelf_error()}.
insert(Table, Key, Value) ->
gleam@result:'try'(
shelf_ffi:insert(
erlang:element(2, Table),
erlang:element(3, Table),
{Key, Value}
),
fun(_) -> maybe_write_through(Table) end
).
-file("src/shelf/set.gleam", 160).
?DOC(" Insert multiple key-value pairs at once.\n").
-spec insert_list(p_set(MKS, MKT), list({MKS, MKT})) -> {ok, nil} |
{error, shelf:shelf_error()}.
insert_list(Table, Entries) ->
gleam@result:'try'(
shelf_ffi:insert_list(
erlang:element(2, Table),
erlang:element(3, Table),
Entries
),
fun(_) -> maybe_write_through(Table) end
).
-file("src/shelf/set.gleam", 172).
?DOC(
" Insert a key-value pair only if the key does not already exist.\n"
"\n"
" Returns `Error(KeyAlreadyPresent)` if the key exists.\n"
).
-spec insert_new(p_set(MKZ, MLA), MKZ, MLA) -> {ok, nil} |
{error, shelf:shelf_error()}.
insert_new(Table, Key, Value) ->
gleam@result:'try'(
shelf_ffi:insert_new(
erlang:element(2, Table),
erlang:element(3, Table),
{Key, Value}
),
fun(_) -> maybe_write_through(Table) end
).
-file("src/shelf/set.gleam", 185).
?DOC(" Delete the entry with the given key.\n").
-spec delete_key(p_set(MLF, any()), MLF) -> {ok, nil} |
{error, shelf:shelf_error()}.
delete_key(Table, Key) ->
gleam@result:'try'(
shelf_ffi:delete_key(erlang:element(2, Table), Key),
fun(_) -> maybe_write_through(Table) end
).
-file("src/shelf/set.gleam", 195).
?DOC(
" Delete a specific key-value pair.\n"
"\n"
" For set tables, this is equivalent to `delete_key` since each key\n"
" has at most one value.\n"
).
-spec delete_object(p_set(MLL, MLM), MLL, MLM) -> {ok, nil} |
{error, shelf:shelf_error()}.
delete_object(Table, Key, Value) ->
gleam@result:'try'(
shelf_ffi:delete_object(erlang:element(2, Table), Key, Value),
fun(_) -> maybe_write_through(Table) end
).
-file("src/shelf/set.gleam", 206).
?DOC(" Delete all entries (keeps the table open).\n").
-spec delete_all(p_set(any(), any())) -> {ok, nil} |
{error, shelf:shelf_error()}.
delete_all(Table) ->
gleam@result:'try'(
shelf_ffi:delete_all(erlang:element(2, Table)),
fun(_) -> maybe_write_through(Table) end
).
-file("src/shelf/set.gleam", 235).
?DOC(
" Discard unsaved ETS changes and reload from DETS.\n"
"\n"
" Clears the ETS table and loads all DETS contents into it.\n"
" Only useful in WriteBack mode — in WriteThrough mode, ETS and\n"
" DETS are always in sync.\n"
).
-spec reload(p_set(any(), any())) -> {ok, nil} | {error, shelf:shelf_error()}.
reload(Table) ->
shelf_ffi:load(erlang:element(2, Table), erlang:element(3, Table)).
-file("src/shelf/set.gleam", 245).
?DOC(
" Flush the DETS write buffer to the OS.\n"
"\n"
" DETS buffers writes internally. This forces them to be written\n"
" to the underlying filesystem. Most useful in WriteThrough mode\n"
" when you want to guarantee durability.\n"
).
-spec sync(p_set(any(), any())) -> {ok, nil} | {error, shelf:shelf_error()}.
sync(Table) ->
shelf_ffi:sync_dets(erlang:element(3, Table)).
-file("src/shelf/set.gleam", 262).
?DOC(
" Atomically increment an integer value by the given amount.\n"
"\n"
" The value associated with the key must be an integer. Returns the\n"
" new value after incrementing. The increment can be negative.\n"
"\n"
" ```gleam\n"
" let assert Ok(Nil) = set.insert(table, \"hits\", 0)\n"
" let assert Ok(1) = set.update_counter(table, \"hits\", 1)\n"
" let assert Ok(3) = set.update_counter(table, \"hits\", 2)\n"
" ```\n"
).
-spec update_counter(p_set(MMP, integer()), MMP, integer()) -> {ok, integer()} |
{error, shelf:shelf_error()}.
update_counter(Table, Key, Amount) ->
gleam@result:'try'(
shelf_ffi:update_counter(erlang:element(2, Table), Key, Amount),
fun(New_val) -> case erlang:element(4, Table) of
write_through ->
_ = shelf_ffi:save(
erlang:element(2, Table),
erlang:element(3, Table)
),
{ok, New_val};
write_back ->
{ok, New_val}
end end
).