-module(trove). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/trove.gleam"). -export([close/1, compact/2, size/1, is_empty/1, dirt_factor/1, file_sync/1, get/2, put/3, delete/2, has_key/2, put_multi/2, delete_multi/2, put_and_delete_multi/3, transaction/3, tx_get/2, tx_put/3, tx_delete/2, tx_has_key/2, with_snapshot/2, snapshot_get/2, snapshot_range/4, range/4, set_auto_compact/2, open/1]). -export_type([open_error/0, auto_compact/0, file_sync/0, config/2, db/2, transaction_result/3]). -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( " An embedded, crash-safe key-value store for Gleam.\n" "\n" " trove stores data in an append-only B+ tree on disk. Every write appends\n" " new nodes and creates a new root — old data is never overwritten. This\n" " gives you crash safety, zero-cost MVCC snapshots, and single-writer /\n" " multiple-reader concurrency backed by an OTP actor.\n" "\n" " ## Quick Start\n" "\n" " ```gleam\n" " import gleam/string\n" " import trove\n" " import trove/codec\n" "\n" " let config = trove.Config(\n" " path: \"./my_db\",\n" " key_codec: codec.string(),\n" " value_codec: codec.string(),\n" " key_compare: string.compare,\n" " auto_compact: trove.NoAutoCompact,\n" " auto_file_sync: trove.AutoSync,\n" " call_timeout: 5000,\n" " )\n" "\n" " let assert Ok(db) = trove.open(config)\n" " trove.put(db, key: \"language\", value: \"gleam\")\n" " let assert Ok(\"gleam\") = trove.get(db, key: \"language\")\n" " trove.close(db)\n" " ```\n" ). -type open_error() :: {directory_error, binary()} | {store_error, binary()} | {lock_error, binary()} | actor_start_error. -type auto_compact() :: {auto_compact, integer(), float()} | no_auto_compact. -type file_sync() :: auto_sync | manual_sync. -type config(LNM, LNN) :: {config, binary(), trove@codec:codec(LNM), trove@codec:codec(LNN), fun((LNM, LNM) -> gleam@order:order()), auto_compact(), file_sync(), integer()}. -opaque db(LNO, LNP) :: {db, gleam@erlang@process:subject(trove@internal@db:message(LNO, LNP)), integer()}. -type transaction_result(LNQ, LNR, LNS) :: {commit, trove@internal@tx:tx(LNQ, LNR), LNS} | {cancel, LNS}. -file("src/trove.gleam", 53). -spec reraise(exception:exception()) -> any(). reraise(Ex) -> {Class, Reason@3} = case Ex of {errored, Reason} -> {<<"error"/utf8>>, Reason}; {thrown, Reason@1} -> {<<"throw"/utf8>>, Reason@1}; {exited, Reason@2} -> {<<"exit"/utf8>>, Reason@2} end, erlang:raise(erlang:binary_to_atom(Class), Reason@3, []). -file("src/trove.gleam", 170). ?DOC( " Close the database and release the file handle and path lock. Does not\n" " fsync — if using `ManualSync`, call `file_sync` before closing to ensure\n" " durability. The `Db` handle must not be used after calling this.\n" "\n" " **Panics** if the store file handle cannot be closed.\n" "\n" " ```gleam\n" " trove.close(db)\n" " ```\n" ). -spec close(db(any(), any())) -> nil. close(Db) -> trove@internal@db:close(erlang:element(2, Db), erlang:element(3, Db)). -file("src/trove.gleam", 185). ?DOC( " Trigger a manual compaction. Rebuilds the store file keeping only live\n" " entries, resetting the dirt factor to zero. Returns `Ok(Nil)` on success\n" " or `Error(reason)` if compaction failed. On failure the database remains\n" " functional with the original store file.\n" "\n" " The timeout is separate from `call_timeout` because compaction can take\n" " much longer than normal operations.\n" "\n" " ```gleam\n" " let assert Ok(Nil) = trove.compact(db, timeout: 60_000)\n" " ```\n" ). -spec compact(db(any(), any()), integer()) -> {ok, nil} | {error, binary()}. compact(Db, Timeout) -> trove@internal@db:compact(erlang:element(2, Db), Timeout). -file("src/trove.gleam", 194). ?DOC( " Returns the number of live entries in the database.\n" "\n" " ```gleam\n" " let count = trove.size(db)\n" " ```\n" ). -spec size(db(any(), any())) -> integer(). size(Db) -> trove@internal@db:size(erlang:element(2, Db), erlang:element(3, Db)). -file("src/trove.gleam", 203). ?DOC( " Returns `True` if the database contains no entries.\n" "\n" " ```gleam\n" " let empty = trove.is_empty(db)\n" " ```\n" ). -spec is_empty(db(any(), any())) -> boolean(). is_empty(Db) -> size(Db) =:= 0. -file("src/trove.gleam", 220). ?DOC( " Returns the current dirt factor: a float between 0.0 and 1.0 that\n" " approximates how much of the store file is occupied by superseded data.\n" " Overwrites and deletes increment the dirt counter because they write new\n" " nodes that make old ones unreachable. New inserts do not increment dirt\n" " since they don't supersede existing data. The formula is\n" " `dirt / (1 + size + dirt)` — the `+1` ensures the result is always\n" " well-defined, even for an empty tree. The value approaches but never\n" " reaches 1.0. Higher values mean more wasted space that compaction would\n" " reclaim.\n" "\n" " ```gleam\n" " let df = trove.dirt_factor(db)\n" " ```\n" ). -spec dirt_factor(db(any(), any())) -> float(). dirt_factor(Db) -> trove@internal@db:dirt_factor(erlang:element(2, Db), erlang:element(3, Db)). -file("src/trove.gleam", 235). ?DOC( " Force an fsync of the store file to disk. Useful when `auto_file_sync`\n" " is set to `ManualSync` and you want to control when data is flushed.\n" "\n" " **Panics** if the fsync system call fails.\n" "\n" " ```gleam\n" " let config = trove.Config(..config, auto_file_sync: trove.ManualSync)\n" " let assert Ok(db) = trove.open(config)\n" " trove.put(db, key: \"hello\", value: \"world\")\n" " trove.file_sync(db)\n" " ```\n" ). -spec file_sync(db(any(), any())) -> nil. file_sync(Db) -> trove@internal@db:file_sync(erlang:element(2, Db), erlang:element(3, Db)). -file("src/trove.gleam", 260). ?DOC( " Look up a key. Returns `Ok(value)` if found, `Error(Nil)` if the\n" " key does not exist.\n" "\n" " **Panics** on store I/O or decode errors (e.g. file corruption).\n" "\n" " ```gleam\n" " let assert Ok(\"world\") = trove.get(db, key: \"hello\")\n" " ```\n" ). -spec get(db(LPQ, LPR), LPQ) -> {ok, LPR} | {error, nil}. get(Db, Key) -> _pipe = trove@internal@db:get( erlang:element(2, Db), Key, erlang:element(3, Db) ), gleam@option:to_result(_pipe, nil). -file("src/trove.gleam", 272). ?DOC( " Insert or update a key-value pair.\n" "\n" " **Panics** on store I/O errors (e.g. disk full, file corruption).\n" "\n" " ```gleam\n" " trove.put(db, key: \"hello\", value: \"world\")\n" " ```\n" ). -spec put(db(LPW, LPX), LPW, LPX) -> nil. put(Db, Key, Value) -> trove@internal@db:put( erlang:element(2, Db), Key, Value, erlang:element(3, Db) ). -file("src/trove.gleam", 283). ?DOC( " Remove a key. No error if the key does not exist.\n" "\n" " **Panics** on store I/O errors (e.g. disk full, file corruption).\n" "\n" " ```gleam\n" " trove.delete(db, key: \"hello\")\n" " ```\n" ). -spec delete(db(LQA, any()), LQA) -> nil. delete(Db, Key) -> trove@internal@db:delete(erlang:element(2, Db), Key, erlang:element(3, Db)). -file("src/trove.gleam", 294). ?DOC( " Check whether a key exists in the database.\n" "\n" " **Panics** on store I/O or decode errors (e.g. file corruption).\n" "\n" " ```gleam\n" " let assert True = trove.has_key(db, key: \"hello\")\n" " ```\n" ). -spec has_key(db(LQE, any()), LQE) -> boolean(). has_key(Db, Key) -> trove@internal@db:has_key(erlang:element(2, Db), Key, erlang:element(3, Db)). -file("src/trove.gleam", 306). ?DOC( " Atomically insert multiple key-value pairs. A single header write covers\n" " the entire batch.\n" "\n" " **Panics** on store I/O errors (e.g. disk full, file corruption).\n" "\n" " ```gleam\n" " trove.put_multi(db, entries: [#(\"a\", \"1\"), #(\"b\", \"2\")])\n" " ```\n" ). -spec put_multi(db(LQI, LQJ), list({LQI, LQJ})) -> nil. put_multi(Db, Entries) -> trove@internal@db:put_multi( erlang:element(2, Db), Entries, erlang:element(3, Db) ). -file("src/trove.gleam", 317). ?DOC( " Atomically delete multiple keys.\n" "\n" " **Panics** on store I/O errors (e.g. disk full, file corruption).\n" "\n" " ```gleam\n" " trove.delete_multi(db, keys: [\"a\", \"b\"])\n" " ```\n" ). -spec delete_multi(db(LQN, any()), list(LQN)) -> nil. delete_multi(Db, Keys) -> trove@internal@db:delete_multi( erlang:element(2, Db), Keys, erlang:element(3, Db) ). -file("src/trove.gleam", 333). ?DOC( " Atomically insert and delete entries in a single operation. Puts are\n" " applied first, then deletes, all under a single header write.\n" "\n" " **Panics** on store I/O errors (e.g. disk full, file corruption).\n" "\n" " ```gleam\n" " trove.put_and_delete_multi(\n" " db,\n" " puts: [#(\"new_key\", \"value\")],\n" " deletes: [\"old_key\"],\n" " )\n" " ```\n" ). -spec put_and_delete_multi(db(LQS, LQT), list({LQS, LQT}), list(LQS)) -> nil. put_and_delete_multi(Db, Puts, Deletes) -> trove@internal@db:put_and_delete_multi( erlang:element(2, Db), Puts, Deletes, erlang:element(3, Db) ). -file("src/trove.gleam", 465). -spec drain_latest_loop( gleam@erlang@process:subject(LRL), gleam@option:option(LRL) ) -> gleam@option:option(LRL). drain_latest_loop(Subject, Acc) -> case gleam@erlang@process:'receive'(Subject, 0) of {ok, Value} -> drain_latest_loop(Subject, {some, Value}); {error, nil} -> Acc end. -file("src/trove.gleam", 461). -spec drain_latest(gleam@erlang@process:subject(LRI)) -> gleam@option:option(LRI). drain_latest(Subject) -> drain_latest_loop(Subject, none). -file("src/trove.gleam", 401). ?DOC( " Run an atomic transaction. The callback receives a `Tx` handle and must\n" " return `Commit(tx:, result: value)` to apply writes or\n" " `Cancel(result: value)` to discard. The transaction holds exclusive\n" " write access for its duration.\n" "\n" " The `timeout` parameter (in milliseconds) controls how long the caller\n" " waits for the transaction to complete, including queue wait time and\n" " callback execution. Choose a value appropriate for your workload —\n" " queued operations or auto-compaction may delay the start, and a\n" " long-running callback consumes the remaining budget.\n" "\n" " **Important:** The callback runs inside the database actor. Do not call\n" " any `trove` functions (such as `get`, `put`, `compact`, etc.) on the\n" " same `Db` handle from within the callback — this will deadlock the actor\n" " until the call timeout fires. Use the `Tx` handle (`tx_get`, `tx_put`,\n" " `tx_delete`) for all reads and writes inside the transaction.\n" "\n" " **Panics** if the `Commit` variant contains a stale or replaced `Tx`\n" " handle (e.g. the original handle instead of the latest one returned by\n" " `tx_put`/`tx_delete`).\n" "\n" " **Non-escaping:** The `Tx` handle is only valid inside the callback.\n" " Do not store it in a variable, send it to another process, or return it —\n" " using a `Tx` after the callback returns will panic or produce undefined\n" " behavior.\n" "\n" " **Timeout semantics:** If the timeout fires while the callback is still\n" " executing, the caller panics but the actor continues running the callback\n" " to completion. This means writes may be durably committed even though the\n" " caller observes a timeout failure. Choose a timeout that accommodates your\n" " expected callback duration and any queued operations ahead of it.\n" "\n" " ```gleam\n" " let result = trove.transaction(db, timeout: 5000, callback: fn(tx) {\n" " let tx = trove.tx_put(tx, key: \"key\", value: \"value\")\n" " trove.Commit(tx:, result: \"done\")\n" " })\n" " ```\n" ). -spec transaction( db(LQY, LQZ), integer(), fun((trove@internal@tx:tx(LQY, LQZ)) -> transaction_result(LQY, LQZ, LRE)) ) -> LRE. transaction(Db, Timeout, Callback) -> Result_subject = gleam@erlang@process:new_subject(), Token = erlang:make_ref(), Run = fun(Transaction) -> Nonce_subject = gleam@erlang@process:new_subject(), Transaction@1 = trove@internal@tx:set_token(Transaction, Token), Transaction@2 = trove@internal@tx:set_nonce_tracker( Transaction@1, {some, Nonce_subject} ), case exception_ffi:rescue(fun() -> Callback(Transaction@2) end) of {ok, {commit, Tx_inner, Value}} -> case exception_ffi:rescue( fun() -> case trove@internal@tx:token(Tx_inner) =:= Token of true -> nil; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"trove"/utf8>>, function => <<"transaction"/utf8>>, line => 418, value => _assert_fail, start => 14517, 'end' => 14566, pattern_start => 14528, pattern_end => 14532}) end, Latest_nonce = drain_latest(Nonce_subject), case Latest_nonce of none -> nil; {some, Expected} -> case trove@internal@tx:nonce(Tx_inner) =:= Expected of true -> nil; _assert_fail@1 -> erlang:error( #{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"trove"/utf8>>, function => <<"transaction"/utf8>>, line => 423, value => _assert_fail@1, start => 14748, 'end' => 14800, pattern_start => 14759, pattern_end => 14763} ) end, nil end end ) of {ok, nil} -> gleam@erlang@process:send(Result_subject, {ok, Value}), {commit_outcome, trove@internal@tx:get_tree(Tx_inner)}; {error, Ex} -> gleam@erlang@process:send(Result_subject, {error, Ex}), cancel_outcome end; {ok, {cancel, Value@1}} -> gleam@erlang@process:send(Result_subject, {ok, Value@1}), cancel_outcome; {error, Ex@1} -> gleam@erlang@process:send(Result_subject, {error, Ex@1}), cancel_outcome end end, trove@internal@db:transaction(erlang:element(2, Db), Timeout, Run), Result@1 = case gleam@erlang@process:'receive'(Result_subject, 0) of {ok, Result} -> Result; _assert_fail@2 -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"trove"/utf8>>, function => <<"transaction"/utf8>>, line => 454, value => _assert_fail@2, start => 15677, 'end' => 15735, pattern_start => 15688, pattern_end => 15698}) end, case Result@1 of {ok, Value@2} -> Value@2; {error, Ex@2} -> reraise(Ex@2) end. -file("src/trove.gleam", 487). ?DOC( " Read a key within a transaction. Sees writes made earlier in the same\n" " transaction. Returns `Error(Nil)` if the key does not exist.\n" "\n" " **Panics** on store I/O or decode errors (e.g. file corruption).\n" "\n" " ```gleam\n" " trove.transaction(db, timeout: 5000, callback: fn(tx) {\n" " let assert Ok(current) = trove.tx_get(tx, key: \"counter\")\n" " let tx = trove.tx_put(tx, key: \"counter\", value: current <> \"!\")\n" " trove.Commit(tx:, result: Nil)\n" " })\n" " ```\n" ). -spec tx_get(trove@internal@tx:tx(LRP, LRQ), LRP) -> {ok, LRQ} | {error, nil}. tx_get(Tx, Key) -> _pipe = trove@internal@tx:get(Tx, Key), gleam@option:to_result(_pipe, nil). -file("src/trove.gleam", 502). ?DOC( " Write a key-value pair within a transaction. Returns the updated `Tx`.\n" "\n" " **Panics** on store I/O errors (e.g. disk full, file corruption).\n" "\n" " ```gleam\n" " trove.transaction(db, timeout: 5000, callback: fn(tx) {\n" " let tx = trove.tx_put(tx, key: \"greeting\", value: \"hello\")\n" " trove.Commit(tx:, result: Nil)\n" " })\n" " ```\n" ). -spec tx_put(trove@internal@tx:tx(LRV, LRW), LRV, LRW) -> trove@internal@tx:tx(LRV, LRW). tx_put(Tx, Key, Value) -> trove@internal@tx:put(Tx, Key, Value). -file("src/trove.gleam", 516). ?DOC( " Delete a key within a transaction. Returns the updated `Tx`.\n" "\n" " **Panics** on store I/O errors (e.g. disk full, file corruption).\n" "\n" " ```gleam\n" " trove.transaction(db, timeout: 5000, callback: fn(tx) {\n" " let tx = trove.tx_delete(tx, key: \"old_key\")\n" " trove.Commit(tx:, result: Nil)\n" " })\n" " ```\n" ). -spec tx_delete(trove@internal@tx:tx(LSB, LSC), LSB) -> trove@internal@tx:tx(LSB, LSC). tx_delete(Tx, Key) -> trove@internal@tx:delete(Tx, Key). -file("src/trove.gleam", 531). ?DOC( " Check whether a key exists within a transaction. Sees writes made\n" " earlier in the same transaction.\n" "\n" " **Panics** on store I/O or decode errors (e.g. file corruption).\n" "\n" " ```gleam\n" " trove.transaction(db, timeout: 5000, callback: fn(tx) {\n" " let exists = trove.tx_has_key(tx, key: \"counter\")\n" " trove.Commit(tx:, result: exists)\n" " })\n" " ```\n" ). -spec tx_has_key(trove@internal@tx:tx(LSH, any()), LSH) -> boolean(). tx_has_key(Tx, Key) -> _pipe = tx_get(Tx, Key), gleam@result:is_ok(_pipe). -file("src/trove.gleam", 552). ?DOC( " Run a callback with a point-in-time snapshot. The snapshot sees the state\n" " of the database at the moment it was acquired — subsequent writes are\n" " invisible to it.\n" "\n" " **Non-escaping:** The `Snapshot` handle is only valid inside the callback.\n" " Do not store it in a variable, send it to another process, or return it —\n" " using a `Snapshot` after the callback returns will panic or produce\n" " undefined behavior because the underlying file handle is closed on exit.\n" "\n" " **Panics** if the snapshot file handle cannot be opened.\n" "\n" " ```gleam\n" " let result = trove.with_snapshot(db, fn(snap) {\n" " trove.snapshot_get(snapshot: snap, key: \"my_key\")\n" " })\n" " // result: Result(String, Nil)\n" " ```\n" ). -spec with_snapshot( db(LSL, LSM), fun((trove@internal@snapshot:snapshot(LSL, LSM)) -> LSR) ) -> LSR. with_snapshot(Db, Callback) -> Snap@1 = case trove@internal@db:acquire_snapshot( erlang:element(2, Db), erlang:element(3, Db) ) of {ok, Snap} -> Snap; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"trove"/utf8>>, function => <<"with_snapshot"/utf8>>, line => 556, value => _assert_fail, start => 18916, 'end' => 19008, pattern_start => 18927, pattern_end => 18935}) end, exception_ffi:defer( fun() -> trove@internal@snapshot:close(Snap@1) end, fun() -> Callback(Snap@1) end ). -file("src/trove.gleam", 573). ?DOC( " Look up a key in a snapshot. Returns `Error(Nil)` if the key does\n" " not exist.\n" "\n" " **Panics** on store read or decode errors (e.g. file corruption).\n" "\n" " ```gleam\n" " trove.with_snapshot(db, fn(snap) {\n" " let assert Ok(value) = trove.snapshot_get(snapshot: snap, key: \"my_key\")\n" " value\n" " })\n" " ```\n" ). -spec snapshot_get(trove@internal@snapshot:snapshot(LSS, LST), LSS) -> {ok, LST} | {error, nil}. snapshot_get(Snapshot, Key) -> _pipe = trove@internal@snapshot:get(Snapshot, Key), gleam@option:to_result(_pipe, nil). -file("src/trove.gleam", 611). ?DOC( " Iterate over entries in a snapshot within optional key bounds.\n" " Returns a lazy `Yielder` that streams entries from disk on demand,\n" " reading only one leaf node at a time.\n" "\n" " The yielder holds a reference to the snapshot's file handle, so it\n" " must be consumed before the snapshot is closed. For large ranges,\n" " prefer this over `range` to avoid loading all entries into memory.\n" "\n" " **Panics** on store read or decode errors during iteration\n" " (e.g. file corruption).\n" "\n" " Use `range.Inclusive(key)` or `range.Exclusive(key)` for bounds,\n" " or `option.None` for unbounded. Use `range.Forward` or `range.Reverse`\n" " for direction.\n" "\n" " ```gleam\n" " import gleam/option.{None, Some}\n" " import gleam/yielder\n" " import trove/range\n" "\n" " let entries = trove.with_snapshot(db, fn(snap) {\n" " let y = trove.snapshot_range(\n" " snapshot: snap,\n" " min: Some(range.Inclusive(\"a\")),\n" " max: None,\n" " direction: range.Forward,\n" " )\n" " yielder.to_list(y)\n" " })\n" " ```\n" ). -spec snapshot_range( trove@internal@snapshot:snapshot(LSY, LSZ), gleam@option:option(trove@range:bound(LSY)), gleam@option:option(trove@range:bound(LSY)), trove@range:direction() ) -> gleam@yielder:yielder({LSY, LSZ}). snapshot_range(Snapshot, Min, Max, Direction) -> trove@internal@snapshot:range(Snapshot, Min, Max, Direction). -file("src/trove.gleam", 645). ?DOC( " Iterate over entries in the database within optional key bounds.\n" " Returns a `List` of key-value pairs.\n" "\n" " For large result sets, use `with_snapshot` and `snapshot_range` instead\n" " to stream entries lazily without loading them all at once.\n" "\n" " **Panics** if the snapshot file handle cannot be opened, or on store\n" " read/decode errors during iteration.\n" "\n" " Use `range.Inclusive(key)` or `range.Exclusive(key)` for bounds,\n" " or `option.None` for unbounded. Use `range.Forward` or `range.Reverse`\n" " for direction.\n" "\n" " ```gleam\n" " import gleam/option.{Some}\n" " import trove/range\n" "\n" " let results =\n" " trove.range(\n" " db,\n" " min: Some(range.Inclusive(\"a\")),\n" " max: Some(range.Exclusive(\"z\")),\n" " direction: range.Forward,\n" " )\n" " ```\n" ). -spec range( db(LTH, LTI), gleam@option:option(trove@range:bound(LTH)), gleam@option:option(trove@range:bound(LTH)), trove@range:direction() ) -> list({LTH, LTI}). range(Db, Min, Max, Direction) -> Snap@1 = case trove@internal@db:acquire_snapshot( erlang:element(2, Db), erlang:element(3, Db) ) of {ok, Snap} -> Snap; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"trove"/utf8>>, function => <<"range"/utf8>>, line => 651, value => _assert_fail, start => 21866, 'end' => 21958, pattern_start => 21877, pattern_end => 21885}) end, exception_ffi:defer( fun() -> trove@internal@snapshot:close(Snap@1) end, fun() -> _pipe = trove@internal@snapshot:range(Snap@1, Min, Max, Direction), gleam@yielder:to_list(_pipe) end ). -file("src/trove.gleam", 658). -spec map_open_error(trove@internal@db:internal_open_error()) -> open_error(). map_open_error(Error) -> case Error of {internal_directory_error, Reason} -> {directory_error, Reason}; {internal_store_error, Reason@1} -> {store_error, Reason@1}; {internal_lock_error, Reason@2} -> {lock_error, Reason@2}; internal_actor_start_error -> actor_start_error end. -file("src/trove.gleam", 667). -spec to_internal_auto_compact(auto_compact()) -> trove@internal@db:auto_compact(). to_internal_auto_compact(Setting) -> case Setting of {auto_compact, Min_dirt, Min_dirt_factor} -> {auto_compact, Min_dirt, Min_dirt_factor}; no_auto_compact -> no_auto_compact end. -file("src/trove.gleam", 244). ?DOC( " Change the auto-compaction setting at runtime.\n" "\n" " ```gleam\n" " trove.set_auto_compact(db, trove.AutoCompact(min_dirt: 100, min_dirt_factor: 0.25))\n" " ```\n" ). -spec set_auto_compact(db(any(), any()), auto_compact()) -> nil. set_auto_compact(Db, Setting) -> trove@internal@db:set_auto_compact( erlang:element(2, Db), to_internal_auto_compact(Setting), erlang:element(3, Db) ). -file("src/trove.gleam", 675). -spec to_internal_file_sync(file_sync()) -> trove@internal@db:file_sync(). to_internal_file_sync(Setting) -> case Setting of auto_sync -> auto_sync; manual_sync -> manual_sync end. -file("src/trove.gleam", 147). ?DOC( " Open a database at the configured path. Creates the directory if it does\n" " not exist. If a store file already exists, recovers the tree from the\n" " latest valid header.\n" "\n" " ```gleam\n" " let config = trove.Config(\n" " path: \"./my_db\",\n" " key_codec: codec.string(),\n" " value_codec: codec.string(),\n" " key_compare: string.compare,\n" " auto_compact: trove.NoAutoCompact,\n" " auto_file_sync: trove.AutoSync,\n" " call_timeout: 5000,\n" " )\n" " let assert Ok(db) = trove.open(config)\n" " ```\n" ). -spec open(config(LOE, LOF)) -> {ok, db(LOE, LOF)} | {error, open_error()}. open(Config) -> _pipe = trove@internal@db:open( erlang:element(2, Config), erlang:element(3, Config), erlang:element(4, Config), erlang:element(5, Config), to_internal_file_sync(erlang:element(7, Config)), to_internal_auto_compact(erlang:element(6, Config)), erlang:element(8, Config) ), _pipe@1 = gleam@result:map( _pipe, fun(_capture) -> {db, _capture, erlang:element(8, Config)} end ), gleam@result:map_error(_pipe@1, fun map_open_error/1).