1use crate::models::{AccountName, CommodityName};
2use chrono::NaiveDate;
3use rust_decimal::Decimal;
4use thiserror::Error;
5
6#[derive(Error, Debug, Clone, PartialEq, Eq)]
10pub enum ModelError {
11 #[error(
13 "invalid account name `{0}`: expected colon-separated segments, the first one starting \
14 with an uppercase letter, each made of letters, digits, `_` or `-`"
15 )]
16 InvalidAccountName(String),
17 #[error(
19 "invalid commodity name `{0}`: expected 2 to 24 characters, starting with an uppercase \
20 letter, ending with an uppercase letter or digit, made of uppercase letters, digits, \
21 `'`, `.`, `_` or `-` in between"
22 )]
23 InvalidCommodityName(String),
24 #[error("invalid metadata key `{0}`: expected only lowercase letters, `_` or `-`")]
26 InvalidMetadataKey(String),
27}
28
29#[derive(Error, Debug, Clone, PartialEq, Eq)]
33pub enum ValidationError {
34 #[error("account `{account}` is already open on {date}")]
36 AccountAlreadyOpen {
37 account: AccountName,
39 date: NaiveDate,
41 },
42 #[error("account `{account}` is not open on {date}")]
44 AccountNotOpen {
45 account: AccountName,
47 date: NaiveDate,
49 },
50 #[error("commodity `{commodity}` is already declared on {date}")]
52 CommodityAlreadyDeclared {
53 commodity: CommodityName,
55 date: NaiveDate,
57 },
58 #[error("commodity `{commodity}` is not declared on {date}")]
60 CommodityNotDeclared {
61 commodity: CommodityName,
63 date: NaiveDate,
65 },
66 #[error("payee `{payee}` is already declared")]
68 PayeeAlreadyDeclared {
69 payee: String,
71 },
72 #[error("payee `{payee}` is not declared (transaction on {date})")]
74 PayeeNotDeclared {
75 payee: String,
77 date: NaiveDate,
79 },
80 #[error(
82 "transaction on {date} has {count} postings without an amount, but at most one posting \
83 can be left without an amount for auto-balancing"
84 )]
85 TooManyPostingsWithoutAmount {
86 count: usize,
88 date: NaiveDate,
90 },
91 #[error(
93 "cannot auto-balance transaction: unable to determine a single commodity for the \
94 missing amount from the other postings on {date} (commodities found: {commodities:?})"
95 )]
96 AmbiguousAutoBalanceCommodity {
97 commodities: Vec<String>,
99 date: NaiveDate,
101 },
102 #[error(
104 "transaction on {date} is not balanced: postings for commodity `{commodity}` sum to \
105 `{sum}` instead of zero"
106 )]
107 TransactionNotBalanced {
108 commodity: String,
110 sum: Decimal,
112 date: NaiveDate,
114 },
115 #[error(
117 "commodity `{commodity}` is not allowed on account `{account}` (posting on {date}): the \
118 account was opened with a restricted list of commodities that does not include it"
119 )]
120 CommodityNotAllowed {
121 account: AccountName,
123 commodity: CommodityName,
125 date: NaiveDate,
127 },
128 #[error(
130 "balance assertion failed on {date}: account `{account}` has a balance of `{actual}` \
131 {commodity}, expected `{expected}` {commodity} (tolerance ~ {tolerance})"
132 )]
133 BalanceAssertionFailed {
134 account: AccountName,
136 commodity: CommodityName,
138 date: NaiveDate,
140 expected: Decimal,
142 actual: Decimal,
144 tolerance: Decimal,
146 },
147}