Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: test

on:
push:
branches:
- main
pull_request:

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
otp-version: "28"
gleam-version: "1.11.1"
rebar3-version: "3"
# elixir-version: "1.15.4"
- run: gleam deps download
- run: gleam test
- run: gleam format --check src test
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@
[![Package Version](https://img.shields.io/hexpm/v/carpenter)](https://hex.pm/packages/carpenter)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/carpenter/)

Bindings for Erlang's [ETS tables](https://www.erlang.org/doc/man/ets.html). Forked and updated from [gts](https://github.com/Lunarmagpie/gts).

If you aren't familiar with ETS tables, [this](https://elixirschool.com/en/lessons/storage/ets) is a good introduction.
Bindings for Erlang's [ETS tables](https://www.erlang.org/doc/man/ets.html).
Forked and updated from [gts](https://github.com/Lunarmagpie/gts).
Carpenter implements support for `set` and `ordered_set`, but does not
supports `bag` or `ordered_bag` ETS table as of now.

If you aren't familiar with ETS tables,
[this](https://elixirschool.com/en/lessons/storage/ets) is a good introduction.

## Quick start

```gleam
import gleam/io
import carpenter/table

pub fn main() {
Expand All @@ -32,7 +34,7 @@ pub fn main() {
// Retrieve a key-value tuple
example
|> table.lookup("hello")
|> io.debug
|> echo

// Delete an object
example
Expand Down
6 changes: 3 additions & 3 deletions gleam.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ repository = { type = "github", user = "grottohub", repo = "carpenter" }
links = [{ title = "Website", href = "https://github.com/grottohub/carpenter" }]

[dependencies]
gleam_stdlib = "~> 0.34 or ~> 1.0"
gleam_erlang = "~> 0.24"
gleam_erlang = ">= 1.0.0 and < 2.0.0"
gleam_stdlib = ">= 0.60.0 and < 2.0.0"

[dev-dependencies]
gleeunit = "~> 1.0"
gleeunit = ">= 1.0.0 and < 2.0.0"
12 changes: 6 additions & 6 deletions manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
# You typically do not need to edit this file

packages = [
{ name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" },
{ name = "gleam_stdlib", version = "0.36.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "C0D14D807FEC6F8A08A7C9EF8DFDE6AE5C10E40E21325B2B29365965D82EB3D4" },
{ name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" },
{ name = "gleam_erlang", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "1124AD3AA21143E5AF0FC5CF3D9529F6DB8CA03E43A55711B60B6B7B3874375C" },
{ name = "gleam_stdlib", version = "1.0.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "960090C2FB391784BB34267B099DC9315CC1B1F6013E7415BC763CEF1905D7D3" },
{ name = "gleeunit", version = "1.10.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "254B697FE72EEAD7BF82E941723918E421317813AC49923EE76A18C788C61E72" },
]

[requirements]
gleam_erlang = { version = "~> 0.24" }
gleam_stdlib = { version = "~> 0.34 or ~> 1.0" }
gleeunit = { version = "~> 1.0" }
gleam_erlang = { version = ">= 1.0.0 and < 2.0.0" }
gleam_stdlib = { version = ">= 0.60.0 and < 2.0.0" }
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
10 changes: 0 additions & 10 deletions src/carpenter.erl

This file was deleted.

42 changes: 34 additions & 8 deletions src/carpenter/internal/ets_bindings.gleam
Original file line number Diff line number Diff line change
@@ -1,41 +1,67 @@
//// [Erlang Documentation](https://www.erlang.org/doc/apps/stdlib/ets.html)

import gleam/dynamic
import gleam/erlang/atom

/// Returns a list of all tables at the node. Named tables are specified by
/// their names, unnamed tables are specified by their table identifiers.
@external(erlang, "ets", "all")
pub fn all() -> List(atom.Atom)

/// Deletes the entire table.
@external(erlang, "ets", "delete")
pub fn drop(table: atom.Atom) -> Nil
pub fn drop(table: atom.Atom) -> Bool

/// Deletes all objects with key from table. This function succeeds
/// even if no objects with key exist.
@external(erlang, "ets", "delete")
pub fn delete_key(table: atom.Atom, key: k) -> Nil
pub fn delete_key(table: atom.Atom, key: k) -> Bool

/// Delete all objects in the ETS table. The operation is guaranteed to be
/// atomic and isolated.
@external(erlang, "ets", "delete_all_objects")
pub fn delete_all_objects(table: atom.Atom) -> Nil
pub fn delete_all_objects(table: atom.Atom) -> Bool

/// Delete the exact object from the ETS table, leaving objects with
/// the same key but other differences (useful for type bag). In a
/// `duplicate_bag` table, all instances of the object are deleted.
@external(erlang, "ets", "delete_object")
pub fn delete_object(table: atom.Atom, object: #(k, v)) -> Nil
pub fn delete_object(table: atom.Atom, object: #(k, v)) -> Bool

/// Inserts all of the objects in list into table.
@external(erlang, "ets", "insert")
pub fn insert(table: atom.Atom, tuple: List(#(k, v))) -> Nil
pub fn insert(table: atom.Atom, tuple: List(#(k, v))) -> Bool

/// Same as `insert` except that instead of overwriting objects with the
/// same key (for `set` or `ordered_set`) or adding more objects with keys
/// already existing in the table (for `bag` and `duplicate_bag`), `False`
/// is returned.
@external(erlang, "ets", "insert_new")
pub fn insert_new(table: atom.Atom, tuple: List(#(k, v))) -> Bool

/// Returns a list of all objects with key in table.
@external(erlang, "ets", "lookup")
pub fn lookup(table: atom.Atom, key: k) -> List(#(k, v))

@external(erlang, "ets", "give_way")
pub fn give_away(table: atom.Atom, pid: pid, gift_data: any) -> Nil
/// Make process `pid` the new owner of the table. If successful, message
/// `{'ETS-TRANSFER',Table,FromPid,GiftData}` is sent to the new owner.
@external(erlang, "ets", "give_away")
pub fn give_away(table: atom.Atom, pid: pid, gift_data: any) -> Bool

@external(erlang, "carpenter", "new_table")
/// Creates a new table and returns a table identifier that can be used in
/// subsequent operations. The table identifier can be sent to other processes
/// so that a table can be shared between different processes within a node.
@external(erlang, "carpenter_ffi", "new_table")
pub fn new_table(
name: atom.Atom,
props: List(dynamic.Dynamic),
) -> Result(atom.Atom, Nil)

/// Works like `lookup`, but does not return the objects. Returns `True` if one
/// or more elements in the table has key, otherwise `False`.
@external(erlang, "ets", "member")
pub fn member(table: atom.Atom, key: k) -> Bool

/// Returns and removes a list of all objects with key in table.
@external(erlang, "ets", "take")
pub fn take(table: atom.Atom, key: k) -> List(#(k, v))
64 changes: 64 additions & 0 deletions src/carpenter/internal/props.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//// Props are options list used in Erlang API. Most of the time, they take the
//// form of options, that can be either atoms, or tuples of atoms (or other
//// data, like numbers, etc.). That module helps dealing with such data
//// structures while providing simple primitives to work with `Option(a)` or
//// `Bool`.
////
//// ```erl
//// % Typically in Erlang, starting an ETS table looks like this.
//// ets:new(mytable, [set, compressed, {write_concurrency, true}]).
//// ```

import gleam/bool
import gleam/dynamic/decode
import gleam/erlang/atom
import gleam/list
import gleam/option.{type Option}

pub type Props =
List(decode.Dynamic)

/// Append a prop to the props, if the value has been set. That function is made
/// to be used with a builder that set fields as optional.
pub fn append(
props: Props,
prop: Option(a),
mapper: fn(a) -> decode.Dynamic,
) -> Props {
prop
|> option.map(mapper)
|> option.map(list.prepend(props, _))
|> option.unwrap(props)
}

/// Append a prop to the props, if the value is true. That function is made
/// to be used with a builder that set fields as false by default.
pub fn append_if(
props: Props,
prop: Bool,
mapper: fn() -> decode.Dynamic,
) -> Props {
use <- bool.guard(when: !prop, return: props)
[mapper(), ..props]
}

/// Helper to generate an atom. Instead of continuously generating it, and
/// coercing it as `Dynamic`, `atom` directly generate a dynamic atom.
pub fn atom(name: String) -> decode.Dynamic {
let value = atom.create(name)
coerce(value)
}

/// Helper to generate a pair of atoms, directly from strings. Simplify code
/// and easy generating of atoms.
pub fn pair(prop: String, value: a) -> decode.Dynamic {
let prop = atom(prop)
let value = coerce(value)
coerce(#(prop, value))
}

/// Used to replace `dynamic.from`, which is now deprecated. Because we're
/// working with Erlang functions that could be highly dynamic, it's often
/// necessary to convert our data structure to `Dynamic`.
@external(erlang, "carpenter_ffi", "coerce")
fn coerce(a: a) -> decode.Dynamic
Loading