database
An outrageously simple set of functions to interact with the BEAM DETS (Disk-based Erlang Term Storage) API.
Good for small projects and POCs.
This project DOES NOT intend to serve as direct bindings to the DETS API, but rather to interact with it in a gleamy way:
- with a simple and concise interface;
- type-safely;
- no unexpected crashes, all errors are values.
Types
Possible error that may occur when using the library.
pub type FileError {
UnableToOpen
UnableToClose
}
Constructors
-
UnableToOpen
A problem occurred when trying to open and lock the .dets file.
-
UnableToClose
A problem ocurred when trying to write into the .dets file and close it.
Operations to perform on a select query.
pub type SelectOption(value) {
Skip
Continue(value)
Done(value)
}
Constructors
-
Skip
Ignores the current value.
-
Continue(value)
Adds the value to the return list.
-
Done(value)
Adds the value to the return list and immediately returns the query.
A reference to an open table, required to interact with said table.
Obtained through the transaction(Table)
function
pub opaque type Transaction(a)
Values
pub fn create_table(
name name: String,
decode_with decoder: decode.Decoder(a),
) -> Result(Table(a), FileError)
Creats a table.
If no .dets file exists for the provided definition, creates one. Otherwise, just checks whether the file is accessible and not corrupted.
Example
pub fn start_database() {
let decoder = {
use name <- database.field(0, decode.string)
use animal <- database.field(1, my_animal_decoder)
decode.success(Pet(name:, animal:))
}
database.create_table(name: "pets", decode_with: decoder)
// -> Ok(Table(Pet))
}
pub fn delete(
transac: Transaction(a),
id: String,
) -> Result(Nil, b)
Deletes a value from a table.
Example
pub fn delete_pet(table: Table(Pet) pet_id: String) {
use ref <- database.transaction(table)
database.delete(ref, pet_id)
}
pub fn drop_table(table: Table(a)) -> Result(Nil, b)
Deletes the entire table file
Example
pub fn destroy_all_pets(table: Table(Pet), password: String) {
case password {
"Yes, I am evil." -> {
database.drop_table(table)
Ok(Nil)
}
_ -> Error(WrongPassword)
}
}
pub fn field(
field_index: Int,
field_decoder: decode.Decoder(t),
next: fn(t) -> decode.Decoder(final),
) -> decode.Decoder(final)
Field decoder
Example
let decoder = {
use name <- database.field(0, decode.string)
use animal <- database.field(1, my_animal_decoder)
decode.success(Pet(name:, animal:))
}
pub fn find(
transac: Transaction(a),
id: String,
) -> option.Option(a)
Finds a value by its index
Example
pub fn play_with_pluto(table: Table(Pet)) {
use ref <- database.transaction(table)
let resp = database.find(ref, known_pluto_id)
case resp {
None -> Error(PlutoNotFoundBlameTheAstronomers)
Some(pluto) -> Ok(play_with(pluto))
}
}
pub fn insert(
transac: Transaction(a),
value: a,
) -> Result(String, b)
Inserts a value into a table and return their generated id.
DETS tables do not have support for update, only for upsert. So if you have to change a value, just insert a new value with the same index, and it will replace the previous value.
Example
pub fn new_pet(table: Table(Pet), animal: Animal, name: String) -> String {
let pet = Pet(name, animal)
use ref <- database.transaction(table)
database.insert(ref, pet)
}
pub fn select(
transac: Transaction(a),
select_fn: fn(#(String, a)) -> SelectOption(b),
) -> List(b)
Searches for somethig on the table.
Example
pub fn fetch_all_parrots(table: Table(Pet)) {
use ref <- database.transaction(table)
use value <- database.select(ref)
case value {
#(_id, Pet(_name, Parrot)) -> Continue(value)
_ -> Skip
}
}
IMPORTANT
DETS tables are not sorted in any deterministic way, so never assume that the last value inserted will be the last one on the table.
pub fn transaction(
table: Table(a),
procedure: fn(Transaction(a)) -> b,
) -> Result(b, FileError)
Allows you to interact with the table.
It opens and locks the .dets file, then execute your operations. Once the operations are done, it writes the changes into the file, closes and releases it.
Example
pub fn is_pet_registered(table: Table(Pet), pet_id: String) {
use ref <- database.transaction(table)
case database.find(ref, pet_id) {
Some(_) -> True
None -> False
}
}