Current section
Files
Jump to
Current section
Files
src/lamb.gleam
import gleam/erlang
import gleam/erlang/atom
import lamb/query.{type Query}
// ------ Table API ------ //
// TODO: Think about enabling the heir option.
// TODO: Think about enabling th give away option (which is independent from heir).
// TODO: Make tables of different types. Set, OrderedSet, Bag, Duplicate Bag.
pub type Table(index, record) {
Table(
reference: TableId,
name: Name,
kind: Kind,
access: Access,
registered: Bool,
)
}
type TableId =
erlang.Reference
type Name =
atom.Atom
pub type Config {
Config(name: String, access: Access, kind: Kind, registered: Bool)
}
pub type Kind {
Set
Bag
}
// TODO: Private and Protected should probably carry a subject.
// Or we should validate the owner and access before making a query.
pub type Access {
Public
Protected
Private
}
pub type Error {
AlreadyRegistered(Name)
}
pub fn create(config: Config) -> Result(Table(index, record), Error) {
// Parameter docs: https://www.erlang.org/doc/apps/stdlib/ets.html#new/2
let Config(name, access, kind, registered) = config
let name = atom.create_from_string(name)
let create = fn() {
let table_id = ffi_create_table(name, access, kind, registered)
Table(table_id, name, kind, access, registered)
}
// If table needs registration check for naming collisions.
case registered, ffi_table_id(name) {
True, Ok(_table_id) -> Error(AlreadyRegistered(name))
_registered, _table_id -> Ok(create())
}
}
pub fn delete(table: Table(index, record)) -> Nil {
// We need to check if table exists otherwise program will crash.
case is_alive(table) {
True -> {
ffi_delete_table(table.reference)
Nil
}
False -> {
Nil
}
}
}
pub fn from_name(name: String) -> Result(TableId, Nil) {
case atom.from_string(name) {
Ok(atom) -> ffi_table_id(atom)
Error(_error) -> Error(Nil)
}
}
pub fn is_alive(table: Table(index, record)) -> Bool {
case ffi_table_id(table.reference) {
Ok(_table_id) -> True
Error(_error) -> False
}
}
@external(erlang, "lamb_erlang_ffi", "table_id")
fn ffi_table_id(table: name_or_ref) -> Result(TableId, Nil)
@external(erlang, "lamb_erlang_ffi", "create_table")
fn ffi_create_table(
name: Name,
access: Access,
kind: Kind,
registered: Bool,
) -> name_or_ref
// Will run into exception if table does not exist.
@external(erlang, "ets", "delete")
fn ffi_delete_table(table: TableId) -> TableId
// ------ Record API ------ //
// TODO: When inserting, updating, deleting, querying. Check the table type and
// who is making the request, who the process owner is to validate that the
// query can be made.
//
// Check if ets already gives us a useful type to work with.
pub fn insert(table: Table(index, record), index: index, record: record) -> Nil {
let table_id = table.reference
let assert True = ffi_insert(table_id, [#(index, record)])
Nil
}
pub fn erase(
table: Table(index, record),
where query: Query(index, record, binding),
) -> Int {
let table_id = table.reference
let query = ffi_update_body(query, True)
ffi_select_delete(table_id, [query])
}
@external(erlang, "ets", "insert")
fn ffi_insert(table: TableId, rows: List(#(index, record))) -> Bool
@external(erlang, "ets", "select_delete")
fn ffi_select_delete(
table: TableId,
queries: List(Query(index, record, binding)),
) -> Int
// ------ Query API ------ //
pub type Partial(record) {
Records(List(record), Step)
End(List(record))
}
pub type Step
pub fn all(
from table: Table(index, record),
where query: Query(index, record, binding),
) -> List(x) {
let table_id = table.reference
ffi_select(table_id, [query])
}
pub fn batch(
from table: Table(index, record),
by limit: Int,
where query: Query(index, record, binding),
) -> Partial(x) {
let table_id = table.reference
ffi_search_partial(table_id, limit, [query])
}
pub fn continue(step: Step) -> Partial(x) {
ffi_search_partial_continue(step)
}
pub fn count(
from table: Table(index, record),
where query: Query(index, record, binding),
) -> Int {
let table_id = table.reference
let query = ffi_update_body(query, True)
ffi_select_count(table_id, [query])
}
@external(erlang, "ets", "select")
fn ffi_select(
table: TableId,
queries: List(Query(index, record, binding)),
) -> List(x)
@external(erlang, "ets", "select_count")
fn ffi_select_count(
table: TableId,
queries: List(Query(index, record, binding)),
) -> Int
@external(erlang, "lamb_erlang_ffi", "search")
fn ffi_search_partial(
table: TableId,
limit: Int,
queries: List(Query(index, record, binding)),
) -> Partial(x)
@external(erlang, "lamb_erlang_ffi", "search")
fn ffi_search_partial_continue(step: Step) -> Partial(x)
@external(erlang, "lamb_query_erlang_ffi", "update_body")
fn ffi_update_body(
query: Query(index, record, binding),
with shape: shape,
) -> Query(index, record, x)