Skip to main content

cala_ledger/ledger/
error.rs

1use sqlx::error::DatabaseError;
2use thiserror::Error;
3
4use crate::{
5    account::error::AccountError, account_set::error::AccountSetError,
6    balance::error::BalanceError, entry::error::EntryError, journal::error::JournalError,
7    posting::PostingError, transaction::error::TransactionError,
8    tx_template::error::TxTemplateError, velocity::error::VelocityError,
9};
10
11#[derive(Error, Debug)]
12pub enum LedgerError {
13    #[error("LedgerError - Sqlx: {0}")]
14    Sqlx(sqlx::Error),
15    #[error("LedgerError - DuplicateKey: {0}")]
16    DuplicateKey(Box<dyn DatabaseError>),
17    #[error("LedgerError - Migrate: {0}")]
18    SqlxMigrate(#[from] sqlx::migrate::MigrateError),
19    #[error("LedgerError - Config: {0}")]
20    ConfigError(String),
21    #[error("LedgerError - AccountError: {0}")]
22    AccountError(#[from] AccountError),
23    #[error("LedgerError - AccountSetError: {0}")]
24    AccountSetError(#[from] AccountSetError),
25    #[error("LedgerError - JournalError: {0}")]
26    JournalError(#[from] JournalError),
27    #[error("LedgerError - TxTemplateError: {0}")]
28    TxTemplateError(#[from] TxTemplateError),
29    #[error("LedgerError - TransactionError: {0}")]
30    TransactionError(#[from] TransactionError),
31    #[error("LedgerError - EntryError: {0}")]
32    EntryError(EntryError),
33    #[error("LedgerError - BalanceError: {0}")]
34    BalanceError(#[from] BalanceError),
35    #[error("LedgerError - VelocityError: {0}")]
36    VelocityError(#[from] VelocityError),
37    #[error("LedgerError - PostingError: {0}")]
38    PostingError(#[from] PostingError),
39    #[error("LedgerError - EcRollupRegistration: {0}")]
40    EcRollupRegistration(#[from] Box<dyn std::error::Error + Send + Sync>),
41    #[error("LedgerError - EcRollupCheckpoint: {0}")]
42    EcRollupCheckpoint(obix::out::HandlerCheckpointError),
43    #[error(
44        "LedgerError - EcCaughtUpTimeout: EC rollup checkpoint {applied} had not reached the \
45         outbox frontier {frontier} after waiting {waited:?}"
46    )]
47    EcCaughtUpTimeout {
48        applied: obix::EventSequence,
49        frontier: obix::EventSequence,
50        waited: std::time::Duration,
51    },
52    #[error(
53        "LedgerError - EntryTargetsAccountSet: an entry may not be posted directly to an \
54         account-set backing account; an account set's balance is derived from its members"
55    )]
56    EntryTargetsAccountSet,
57}
58
59impl From<EntryError> for LedgerError {
60    fn from(e: EntryError) -> Self {
61        match e {
62            // Surface the posting-flow domain error at the top level so
63            // callers don't have to dig through the entry-error nesting.
64            EntryError::EntryTargetsAccountSet => Self::EntryTargetsAccountSet,
65            other => Self::EntryError(other),
66        }
67    }
68}
69
70/// Manual, not `#[from]`: the timeout case is remapped so callers
71/// destructure ledger vocabulary (`applied`) rather than obix's
72/// (`checkpoint`). Everything else passes through.
73impl From<obix::out::HandlerCheckpointError> for LedgerError {
74    fn from(e: obix::out::HandlerCheckpointError) -> Self {
75        match e {
76            obix::out::HandlerCheckpointError::CaughtUpTimeout {
77                checkpoint,
78                target,
79                waited,
80            } => Self::EcCaughtUpTimeout {
81                applied: checkpoint,
82                frontier: target,
83                waited,
84            },
85            other => Self::EcRollupCheckpoint(other),
86        }
87    }
88}
89
90impl From<sqlx::Error> for LedgerError {
91    fn from(e: sqlx::Error) -> Self {
92        match e {
93            sqlx::Error::Database(err) if err.message().contains("duplicate key") => {
94                LedgerError::DuplicateKey(err)
95            }
96            e => LedgerError::Sqlx(e),
97        }
98    }
99}