Packages

Persistent ETS tables backed by DETS — fast in-memory access with automatic disk persistence for the BEAM

Current section

Files

Jump to
shelf src shelf@bag.erl
Raw

src/shelf@bag.erl

-module(shelf@bag).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/shelf/bag.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, delete_key/2, delete_object/3, delete_all/1, reload/1, sync/1]).
-export_type([p_bag/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_bag(LNB, LNC) :: {p_bag,
shelf@internal:ets_ref(),
shelf@internal:dets_ref(),
shelf:write_mode()} |
{gleam_phantom, LNB, LNC}.
-file("src/shelf/bag.gleam", 35).
?DOC(" Open a persistent bag table with full configuration.\n").
-spec open_config(shelf:config()) -> {ok, p_bag(any(), any())} |
{error, shelf:shelf_error()}.
open_config(Config) ->
{config, Name, Path, Write_mode} = Config,
_pipe = shelf_ffi:open_bag(Name, Path),
gleam@result:map(
_pipe,
fun(Refs) ->
{p_bag,
erlang:element(1, Refs),
erlang:element(2, Refs),
Write_mode}
end
).
-file("src/shelf/bag.gleam", 47).
?DOC(
" Open a persistent bag table with defaults (WriteBack mode).\n"
"\n"
" ```gleam\n"
" let assert Ok(table) = bag.open(\"tags\", \"data/tags.dets\")\n"
" ```\n"
).
-spec open(binary(), binary()) -> {ok, p_bag(any(), any())} |
{error, shelf:shelf_error()}.
open(Name, Path) ->
open_config(shelf:config(Name, Path)).
-file("src/shelf/bag.gleam", 56).
?DOC(" Close the table, saving all data to disk.\n").
-spec close(p_bag(any(), any())) -> {ok, nil} | {error, shelf:shelf_error()}.
close(Table) ->
shelf_ffi:close(erlang:element(2, Table), erlang:element(3, Table)).
-file("src/shelf/bag.gleam", 67).
?DOC(
" Use a table within a callback, ensuring it is closed afterward.\n"
"\n"
" ```gleam\n"
" use table <- bag.with_table(\"tags\", \"data/tags.dets\")\n"
" bag.insert(table, \"color\", \"red\")\n"
" ```\n"
).
-spec with_table(
binary(),
binary(),
fun((p_bag(any(), any())) -> {ok, LNZ} | {error, shelf:shelf_error()})
) -> {ok, LNZ} | {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/bag.gleam", 84).
?DOC(
" Look up all values for a key.\n"
"\n"
" Returns `Error(NotFound)` if the key does not exist.\n"
).
-spec lookup(p_bag(LOE, LOF), LOE) -> {ok, list(LOF)} |
{error, shelf:shelf_error()}.
lookup(Table, Key) ->
shelf_ffi:lookup_bag(erlang:element(2, Table), Key).
-file("src/shelf/bag.gleam", 90).
?DOC(" Check if a key exists without returning the values.\n").
-spec member(p_bag(LOL, any()), LOL) -> {ok, boolean()} |
{error, shelf:shelf_error()}.
member(Table, Key) ->
shelf_ffi:member(erlang:element(2, Table), Key).
-file("src/shelf/bag.gleam", 98).
?DOC(
" Return all key-value pairs as a list.\n"
"\n"
" **Warning**: loads entire table into memory.\n"
).
-spec to_list(p_bag(LOR, LOS)) -> {ok, list({LOR, LOS})} |
{error, shelf:shelf_error()}.
to_list(Table) ->
shelf_ffi:to_list(erlang:element(2, Table)).
-file("src/shelf/bag.gleam", 104).
?DOC(" Fold over all entries. Order is unspecified.\n").
-spec fold(p_bag(LOY, LOZ), LPC, fun((LPC, LOY, LOZ) -> LPC)) -> {ok, LPC} |
{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/bag.gleam", 117).
?DOC(" Return the number of objects stored.\n").
-spec size(p_bag(any(), any())) -> {ok, integer()} |
{error, shelf:shelf_error()}.
size(Table) ->
shelf_ffi:size(erlang:element(2, Table)).
-file("src/shelf/bag.gleam", 178).
?DOC(" Snapshot the current ETS contents to DETS.\n").
-spec save(p_bag(any(), any())) -> {ok, nil} | {error, shelf:shelf_error()}.
save(Table) ->
shelf_ffi:save(erlang:element(2, Table), erlang:element(3, Table)).
-file("src/shelf/bag.gleam", 196).
-spec maybe_write_through(p_bag(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/bag.gleam", 125).
?DOC(" Insert a key-value pair. Duplicate key-value pairs are ignored.\n").
-spec insert(p_bag(LPL, LPM), LPL, LPM) -> {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/bag.gleam", 136).
?DOC(" Insert multiple key-value pairs.\n").
-spec insert_list(p_bag(LPR, LPS), list({LPR, LPS})) -> {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/bag.gleam", 148).
?DOC(" Delete all values for the given key.\n").
-spec delete_key(p_bag(LPY, any()), LPY) -> {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/bag.gleam", 158).
?DOC(
" Delete a specific key-value pair.\n"
"\n"
" Only the exact matching pair is removed. Other values for the same\n"
" key are preserved.\n"
).
-spec delete_object(p_bag(LQE, LQF), LQE, LQF) -> {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/bag.gleam", 169).
?DOC(" Delete all entries (keeps the table open).\n").
-spec delete_all(p_bag(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/bag.gleam", 184).
?DOC(" Discard unsaved ETS changes and reload from DETS.\n").
-spec reload(p_bag(any(), any())) -> {ok, nil} | {error, shelf:shelf_error()}.
reload(Table) ->
shelf_ffi:load(erlang:element(2, Table), erlang:element(3, Table)).
-file("src/shelf/bag.gleam", 190).
?DOC(" Flush the DETS write buffer to the OS.\n").
-spec sync(p_bag(any(), any())) -> {ok, nil} | {error, shelf:shelf_error()}.
sync(Table) ->
shelf_ffi:sync_dets(erlang:element(3, Table)).