Skip to main content

Module identifiers

Module identifiers 

Source
Expand description

Domain-typed identifiers: Account, Currency, Tag, Link.

These newtype wrappers around InternedStr give the type system enough vocabulary to distinguish the different kinds of identifier the beancount AST carries. Pre-newtype, every identifier was just an InternedStr — passing an account where a currency was expected (or vice versa) compiled fine, and the bug surfaced only at runtime via wrong-but-validly-shaped string matching. Now the same mistake is a type error.

§Design

Each newtype is a transparent wrapper:

  • Deref<Target = str> so calls like account.starts_with("Assets:") work without .as_str() everywhere.
  • AsRef<str> and Borrow<str> so HashMap lookups by &str keep working (some_map.get("Assets:Bank") where the map is keyed by Account).
  • PartialEq against str / &str / String / InternedStr / the newtype’s own type, so account == "Assets:Bank" keeps reading naturally without coercion.
  • From<&str>, From<String>, From<InternedStr> for construction at call sites that have a string and need the typed form.
  • Hash delegates to the inner InternedStr’s hash, so HashMap<Account, V> and HashMap<InternedStr, V> produce the same bucketing for the same underlying string.

What you DON’T get for free is cross-newtype assignment:

fn want_currency(_: Currency) {}
let acct = Account::from("Assets:Bank");
want_currency(acct); // ← type error

Conversions between newtypes are deliberate (Currency::from(account.into_interned())) so the compiler can flag accidental crossings.

§When to use which

All four newtypes — Currency, Account, Tag, and Link — are fully plumbed through the AST, including MetaValue variants:

  • Currency: Commodity.currency, Open.currencies entries, Amount.currency, CostSpec.currency, Price.currency, IncompleteAmount::CurrencyOnly, MetaValue::Currency.
  • Account: Open.account, Close.account, Balance.account, Pad.account / source_account, Note.account, Document.account, Posting.account, MetaValue::Account.
  • Tag: Transaction.tags entries, pushtag/poptag stack, Document.tags, MetaValue::Tag.
  • Link: Transaction.links entries, Document.links, MetaValue::Link.

The plugin wire-format type rustledger_plugin_types::MetaValueData deliberately keeps String payloads — plugin-types is a minimal WASM-compatible crate that does not depend on rustledger-core, and plugins run without access to the workspace interner anyway. The convert boundary (rustledger_plugin::convert::from_wrapper) wraps the incoming strings in fresh Arc<str>s; the cross-file canonicalization to one Arc<str> per identifier string happens later in rustledger_loader::dedup::reintern_directives, which walks both AST identifier fields and MetaValue::* payloads inside metadata.

Structs§

Account
Domain-typed identifier for a beancount account name (e.g. Assets:Cash:USD). See the module docs for rationale.
AccountResolver
The resolver for an archived Account
ArchivedAccount
An archived Account
ArchivedCurrency
An archived Currency
ArchivedLink
An archived Link
ArchivedTag
An archived Tag
Currency
Domain-typed identifier for a currency code (e.g. USD, EUR, AAPL). See the module docs for rationale.
CurrencyResolver
The resolver for an archived Currency
Link
Domain-typed identifier for a beancount link (e.g. ^invoice-2024-01). See the module docs for rationale.
LinkResolver
The resolver for an archived Link
Tag
Domain-typed identifier for a beancount tag (e.g. #travel). See the module docs for rationale.
TagResolver
The resolver for an archived Tag

Constants§

ACCOUNT_TYPES
The five Beancount root account types, in declaration order.

Functions§

account_type
The lowercased root account type for account — the segment before the first : — or "unknown" if it is not one of ACCOUNT_TYPES.
is_subaccount_or_equal
Returns true if child is the same account as parent, or a sub-account of it.