slate

Type-safe Gleam wrapper for Erlang DETS (Disk Erlang Term Storage).

DETS provides persistent key-value storage backed by files on disk. Tables survive process crashes and node restarts. DETS is built into OTP — no external database or dependency is needed.

Table Types

Quick Start

import gleam/dynamic/decode
import slate/set

let assert Ok(table) = set.open("data/cache.dets",
  key_decoder: decode.string, value_decoder: decode.string)
let assert Ok(Nil) = set.insert(table, "key", "value")
let assert Ok(value) = set.lookup(table, key: "key")
let assert Ok(Nil) = set.close(table)

Limitations

Types

Access mode for opening tables.

pub type AccessMode {
  ReadWrite
  ReadOnly
}

Constructors

  • ReadWrite

    Read and write access (default)

  • ReadOnly

    Read-only access — writes will return AccessDenied

Errors that can occur during DETS operations.

Match on the explicit variants for expected cases such as NotFound, AccessDenied(_), or TypeMismatch(_).

Treat UnexpectedError(detail) as diagnostic output for logs and debugging only. Its string detail is not part of slate’s stable API contract. Use error_code or error_message when you want a stable classifier or a user-facing message.

pub type DetsError {
  NotFound
  FileNotFound(FileErrorContext)
  AlreadyOpen(FileErrorContext)
  TableDoesNotExist
  FileSizeLimitExceeded(FileErrorContext)
  KeyAlreadyPresent
  AccessDenied(FileErrorContext)
  TypeMismatch(FileErrorContext)
  TableNamePoolExhausted
  NotADetsFile(FileErrorContext)
  NeedsRepair(FileErrorContext)
  DecodeErrors(List(decode.DecodeError))
  UnexpectedError(String)
}

Constructors

  • NotFound

    No value found for the given key

  • FileNotFound(FileErrorContext)

    Table file or a parent directory does not exist

  • AlreadyOpen(FileErrorContext)

    Table is already open with a different configuration

  • TableDoesNotExist

    The table does not exist (not open)

  • FileSizeLimitExceeded(FileErrorContext)

    File exceeds the 2 GB DETS limit

  • KeyAlreadyPresent

    Key already exists (for insert_new)

  • AccessDenied(FileErrorContext)

    File access denied, or write operation attempted on a read-only table

  • TypeMismatch(FileErrorContext)

    Table type or key position mismatch (e.g., opening a set file as a bag)

  • TableNamePoolExhausted

    All internal table name slots are in use; close unused tables to free slots

  • NotADetsFile(FileErrorContext)

    File exists but is not a valid DETS file

  • NeedsRepair(FileErrorContext)

    File was not closed cleanly and NoRepair was requested

  • DecodeErrors(List(decode.DecodeError))

    Data read from disk did not match the expected Gleam types

  • UnexpectedError(String)

    Unexpected OTP or Erlang-level error for logging and diagnostics only.

Diagnostic context for an expected file error.

path is the filename reported by OTP, not a table name. For a pathless AlreadyOpen error, open operations supply the path they passed to OTP. Opens normally report an absolute path; is_dets_file can report a relative path. None means OTP supplied no single filename (for example, a pathless error or a rename failure with two filenames). No table lookup is needed, so context remains usable after the table closes.

reason is the lower-level Erlang reason formatted for diagnostics, such as "enoent", "{error,eacces}", "access_mode", or "keypos_mismatch". Rename failures retain both filenames in this diagnostic string. Do not parse it as a stable classifier. Both fields can contain sensitive details: use error_code and error_message for safe external output.

pub type FileErrorContext {
  FileErrorContext(path: option.Option(String), reason: String)
}

Constructors

Auto-repair policy for improperly closed tables.

pub type RepairPolicy {
  AutoRepair
  ForceRepair
  NoRepair
}

Constructors

  • AutoRepair

    Repair automatically if needed (default)

  • ForceRepair

    Force repair even if file appears clean

  • NoRepair

    Don’t repair, return error instead

Information about an open DETS table.

file_path is the absolute path used by open, with . and .. segments normalized. Symlinks are not resolved.

pub type TableInfo {
  TableInfo(file_size: Int, object_count: Int, file_path: String)
}

Constructors

  • TableInfo(file_size: Int, object_count: Int, file_path: String)

Values

pub fn error_code(of error: DetsError) -> String

Return a stable machine-readable code for a DetsError.

This is useful when you want to log, branch on, or serialize error categories without relying on the detail string in UnexpectedError(_).

pub fn error_message(of error: DetsError) -> String

Return a concise user-facing description for a DetsError.

File context and UnexpectedError(_) details are intentionally omitted so callers can safely surface messages without leaking paths or OTP details.

pub fn is_dets_file(path: String) -> Result(Bool, DetsError)

Check whether the given file is a valid DETS file.

Returns Ok(True) if the file is a valid DETS file, Ok(False) if it exists but is not a DETS file, or an error if the file cannot be read.

let assert Ok(True) = slate.is_dets_file("data/cache.dets")
let assert Ok(False) = slate.is_dets_file("README.md")
Search Document