Behaviour that every Beancount execution backend must implement.
The behaviour is the seam that lets beancount_ex swap its backend without
changing the public Beancount.* API:
Beancount -> Engine.CLI (v0.1, wraps real Beancount)
Beancount -> Engine.Elixir (future, native)
Beancount -> Engine.Rust (future, native)The engine is selected via configuration:
config :beancount_ex, engine: Beancount.Engine.CLI
Summary
Callbacks
Check a .bean document, returning a normalized Beancount.Result.
Check a .bean file on disk, returning a normalized Beancount.Result.
Run a BQL query against a .bean document.
Render a directive stream into .bean text.
Functions
Return the currently configured engine module.
Callbacks
@callback check(binary()) :: {:ok, Beancount.Result.t()} | {:error, Beancount.Result.t()}
Check a .bean document, returning a normalized Beancount.Result.
Examples
Requires bean-check on PATH, or use Beancount.Engine.Elixir.check/1:
text = "2026-01-01 open Assets:Bank USD\n"
if Beancount.Checker.available?() do
{:ok, %Beancount.Result{}} = Beancount.Engine.CLI.check(text)
end
@callback check_file(Path.t()) :: {:ok, Beancount.Result.t()} | {:error, Beancount.Result.t()}
Check a .bean file on disk, returning a normalized Beancount.Result.
Engines that shell out to CLI tools should preserve the file path so
include directives resolve relative to the ledger file.
Examples
path = Path.join(System.tmp_dir!(), "engine_check.bean")
File.write!(path, "2026-01-01 open Assets:Bank USD\n")
if Beancount.Checker.available?() do
{:ok, _} = Beancount.Engine.CLI.check_file(path)
end
@callback query(binary(), binary()) :: {:ok, Beancount.Query.Result.t()} | {:error, Beancount.Result.t()}
Run a BQL query against a .bean document.
The first argument is the ledger text, the second is a Beancount Query
Language (BQL) string. Returns a neutral, engine-independent
Beancount.Query.Result on success, or a Beancount.Result describing the
failure otherwise.
Examples
text = """
2026-01-01 open Assets:Bank USD
2026-01-01 open Income:Salary USD
2026-01-01 open Equity:Opening USD
2026-01-31 * "Employer" "Salary"
Assets:Bank 100 USD
Income:Salary -100 USD
"""
{:ok, %Beancount.Query.Result{columns: cols}} =
Beancount.Engine.Elixir.query(text, "SELECT account, sum(position) AS balance GROUP BY account ORDER BY account")
cols
# => ["account", "balance"]
Render a directive stream into .bean text.
Examples
iex> Beancount.Engine.CLI.render([Beancount.open(~D[2026-01-01], "Assets:Bank", ["USD"])])
"2026-01-01 open Assets:Bank USD\n"
Functions
@spec configured() :: module()
Return the currently configured engine module.
Examples
iex> Beancount.Engine.configured()
Beancount.Engine.CLI