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.erl
Raw

src/shelf.erl

-module(shelf).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/shelf.gleam").
-export([config/3, write_mode/2, get_name/1, get_path/1, get_base_directory/1, get_write_mode/1, validate_path/2]).
-export_type([shelf_error/0, write_mode/0, config/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.
-type shelf_error() :: not_found |
key_already_present |
table_closed |
not_owner |
{file_error, binary()} |
name_conflict |
{invalid_path, binary()} |
file_size_limit_exceeded |
{type_mismatch, list(gleam@dynamic@decode:decode_error())} |
{erlang_error, binary()}.
-type write_mode() :: write_back | write_through.
-opaque config() :: {config, binary(), binary(), binary(), write_mode()}.
-file("src/shelf.gleam", 148).
?DOC(
" Create a config with defaults (WriteBack mode).\n"
"\n"
" The `name` is a diagnostic label for the table — it is not used as an\n"
" ETS table name and does not need to be unique. Multiple tables can\n"
" share the same name as long as they use different DETS file paths.\n"
"\n"
" The `base_directory` restricts DETS file paths to prevent directory\n"
" traversal attacks. The `path` is resolved relative to `base_directory`.\n"
"\n"
" ```gleam\n"
" let conf = shelf.config(name: \"users\", path: \"users.dets\",\n"
" base_directory: \"/app/data\")\n"
" ```\n"
).
-spec config(binary(), binary(), binary()) -> config().
config(Name, Path, Base_directory) ->
{config, Name, Path, Base_directory, write_back}.
-file("src/shelf.gleam", 165).
?DOC(
" Set the write mode on a config.\n"
"\n"
" ```gleam\n"
" let conf =\n"
" shelf.config(name: \"users\", path: \"users.dets\",\n"
" base_directory: \"/app/data\")\n"
" |> shelf.write_mode(shelf.WriteThrough)\n"
" ```\n"
).
-spec write_mode(config(), write_mode()) -> config().
write_mode(Config, Mode) ->
{config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
Mode}.
-file("src/shelf.gleam", 173).
?DOC(false).
-spec get_name(config()) -> binary().
get_name(Config) ->
erlang:element(2, Config).
-file("src/shelf.gleam", 178).
?DOC(false).
-spec get_path(config()) -> binary().
get_path(Config) ->
erlang:element(3, Config).
-file("src/shelf.gleam", 183).
?DOC(false).
-spec get_base_directory(config()) -> binary().
get_base_directory(Config) ->
erlang:element(4, Config).
-file("src/shelf.gleam", 188).
?DOC(false).
-spec get_write_mode(config()) -> write_mode().
get_write_mode(Config) ->
erlang:element(5, Config).
-file("src/shelf.gleam", 197).
?DOC(false).
-spec validate_path(binary(), binary()) -> {ok, binary()} |
{error, shelf_error()}.
validate_path(Path, Base_directory) ->
case gleam_stdlib:contains_string(Path, <<"\x{0}"/utf8>>) of
true ->
{error, {invalid_path, <<"Path contains null bytes"/utf8>>}};
false ->
case gleam_stdlib:contains_string(Base_directory, <<"\x{0}"/utf8>>) of
true ->
{error,
{invalid_path,
<<"Base directory contains null bytes"/utf8>>}};
false ->
shelf_ffi:validate_path(Path, Base_directory)
end
end.