From a614600a87f5f2b38af13a72f435f655d6eae7b0 Mon Sep 17 00:00:00 2001 From: Stephen Waits <1+swaits@noreply.git.swaits.com> Date: Sun, 11 Oct 2026 00:46:13 +0000 Subject: [PATCH] Initial commit: wanderbooks accounting semantics engine --- crates/ledger-core/src/error.rs | 78 +++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 crates/ledger-core/src/error.rs diff --git a/crates/ledger-core/src/error.rs b/crates/ledger-core/src/error.rs new file mode 100644 index 0000000..cd7ef9d --- /dev/null +++ b/crates/ledger-core/src/error.rs @@ -0,0 +1,78 @@ +//! Error type for the ledger engine. + +use rust_decimal::Decimal; +use thiserror::Error; + +/// Every way a ledger command can fail. +/// +/// Variants are part of the engine's contract: agents adding features add +/// variants here rather than inventing ad-hoc error reporting. +#[derive(Debug, Error, PartialEq, Eq)] +pub enum LedgerError { + /// A journal entry must have at least two lines (one debit, one credit). + #[error("journal entry has fewer than two lines")] + TooFewLines, + + /// Debits and credits must be exactly equal. No epsilon, ever. + #[error("journal entry does not balance: debits {debits} != credits {credits}")] + UnbalancedEntry { + /// Sum of debit lines. + debits: Decimal, + /// Sum of credit lines. + credits: Decimal, + }, + + /// Zero-amount lines are noise; they are rejected, not ignored. + #[error("journal line has a zero amount")] + ZeroAmountLine, + + /// The referenced account does not exist in the chart of accounts. + #[error("unknown account id {0}")] + UnknownAccount(u64), + + /// Placeholder (header) accounts organize the chart; they take no postings. + #[error("account '{0}' is a placeholder and cannot take postings")] + PlaceholderAccount(String), + + /// All lines of an entry (and their accounts) must share one currency. + #[error("currency mismatch: expected {expected}, found {found}")] + CurrencyMismatch { + /// The currency that was expected. + expected: String, + /// The currency that was found. + found: String, + }, + + /// Currency codes are three ASCII letters (ISO 4217 alpha-3). + #[error("invalid currency code '{0}': must be three ASCII letters")] + InvalidCurrencyCode(String), + + /// Posted entries are immutable. This is load-bearing: it is what makes + /// the ledger an audit trail instead of a spreadsheet. + #[error("entry {0} is already posted and immutable")] + EntryAlreadyPosted(u64), + + /// Only posted entries can be reversed. + #[error("entry {0} is not posted yet")] + EntryNotPosted(u64), + + /// The referenced journal entry does not exist. + #[error("entry {0} does not exist")] + UnknownEntry(u64), + + /// Account codes must be unique within a chart of accounts. + #[error("account code '{0}' is already in use")] + DuplicateAccountCode(String), + + /// Account codes must not be empty. + #[error("account code must not be empty")] + EmptyAccountCode, + + /// The referenced parent account does not exist. + #[error("unknown parent account id {0}")] + UnknownParentAccount(u64), + + /// Any attempt to mutate a posted entry: add lines, delete it, edit it. + #[error("cannot modify a posted entry; post a reversing entry instead")] + PostedEntryImmutable, +}