-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 ).