1pub mod decoration;
4pub mod display;
5pub mod expr;
6pub mod plain;
7pub mod tracked;
8
9use std::{borrow::Cow, fmt};
10
11use bounded_static::ToStatic;
12use chrono::{NaiveDate, NaiveDateTime};
13use derive_where::derive_where;
14
15#[cfg(test)]
16use bounded_static::ToBoundedStatic;
17
18use decoration::Decoration;
19
20#[derive_where(Debug, PartialEq, Eq)]
22pub enum LedgerEntry<'i, Deco: Decoration> {
23 Txn(Transaction<'i, Deco>),
25 Comment(TopLevelComment<'i>),
27 ApplyTag(ApplyTag<'i>),
29 EndApplyTag,
31 Include(IncludeFile<'i>),
33 Account(AccountDeclaration<'i>),
35 Commodity(CommodityDeclaration<'i>),
37}
38
39impl LedgerEntry<'_, plain::Ident> {
40 #[cfg(test)]
41 pub(crate) fn to_static(&self) -> LedgerEntry<'static, plain::Ident> {
42 match self {
43 LedgerEntry::Txn(v) => LedgerEntry::Txn(v.to_static()),
44 LedgerEntry::Comment(v) => LedgerEntry::Comment(v.to_static()),
45 LedgerEntry::ApplyTag(v) => LedgerEntry::ApplyTag(v.to_static()),
46 LedgerEntry::EndApplyTag => LedgerEntry::EndApplyTag,
47 LedgerEntry::Include(v) => LedgerEntry::Include(v.to_static()),
48 LedgerEntry::Account(v) => LedgerEntry::Account(v.to_static()),
49 LedgerEntry::Commodity(v) => LedgerEntry::Commodity(v.to_static()),
50 }
51 }
52}
53
54#[derive(Debug, PartialEq, Eq, ToStatic)]
56pub struct TopLevelComment<'i>(pub Cow<'i, str>);
57
58#[derive(Debug, PartialEq, Eq, ToStatic)]
60pub struct ApplyTag<'i> {
61 pub key: Cow<'i, str>,
62 pub value: Option<MetadataValue<'i>>,
63}
64
65#[derive(Debug, PartialEq, Eq, ToStatic)]
68pub struct IncludeFile<'i>(pub Cow<'i, str>);
69
70#[derive(Debug, PartialEq, Eq, ToStatic)]
72pub struct AccountDeclaration<'i> {
73 pub name: Cow<'i, str>,
75 pub details: Vec<AccountDetail<'i>>,
77}
78
79#[derive(Debug, PartialEq, Eq, ToStatic)]
81pub enum AccountDetail<'i> {
82 Comment(Cow<'i, str>),
84 Note(Cow<'i, str>),
87 Alias(Cow<'i, str>),
89}
90
91#[derive(Debug, PartialEq, Eq, ToStatic)]
93pub struct CommodityDeclaration<'i> {
94 pub name: Cow<'i, str>,
96 pub details: Vec<CommodityDetail<'i>>,
98}
99
100#[derive(Debug, PartialEq, Eq, ToStatic)]
102pub enum CommodityDetail<'i> {
103 Comment(Cow<'i, str>),
105 Note(Cow<'i, str>),
108 Alias(Cow<'i, str>),
111 Format(expr::Amount<'i>),
113}
114
115#[derive_where(Debug, PartialEq, Eq)]
117pub struct Transaction<'i, Deco: Decoration> {
118 pub date: NaiveDate,
120 pub effective_date: Option<NaiveDate>,
122 pub clear_state: ClearState,
124 pub code: Option<Cow<'i, str>>,
126 pub payee: Cow<'i, str>,
128 pub posts: Vec<Deco::Decorated<Posting<'i, Deco>>>,
130 pub metadata: Vec<Metadata<'i>>,
132}
133
134impl Transaction<'_, plain::Ident> {
135 #[cfg(test)]
136 fn to_static(&self) -> Transaction<'static, plain::Ident> {
137 let mut posts = Vec::new();
138 for p in &self.posts {
139 posts.push(p.to_static());
140 }
141 Transaction {
142 date: self.date,
143 effective_date: self.effective_date,
144 clear_state: self.clear_state,
145 code: self.code.to_static(),
146 payee: self.payee.to_static(),
147 posts,
148 metadata: self.metadata.to_static(),
149 }
150 }
151}
152
153impl<'i, Deco: Decoration> Transaction<'i, Deco> {
154 pub fn new<T>(date: NaiveDate, payee: T) -> Self
156 where
157 T: Into<Cow<'i, str>>,
158 {
159 Transaction {
160 date,
161 effective_date: None,
162 clear_state: ClearState::Uncleared,
163 code: None,
164 payee: payee.into(),
165 metadata: Vec::new(),
166 posts: Vec::new(),
167 }
168 }
169}
170
171#[derive_where(Debug, PartialEq, Eq)]
172pub struct Posting<'i, Deco: Decoration> {
174 pub account: Deco::Decorated<Cow<'i, str>>,
176 pub clear_state: ClearState,
178 pub amount: Option<PostingAmount<'i, Deco>>,
180 pub balance: Option<Deco::Decorated<expr::ValueExpr<'i>>>,
182 pub metadata: Vec<Metadata<'i>>,
184}
185
186impl Posting<'_, plain::Ident> {
187 #[cfg(test)]
188 fn to_static(&self) -> Posting<'static, plain::Ident> {
189 Posting {
190 account: self.account.to_static(),
191 clear_state: self.clear_state,
192 amount: self.amount.as_ref().map(|x| x.to_static()),
193 balance: self.balance.to_static(),
194 metadata: self.metadata.to_static(),
195 }
196 }
197}
198
199impl<'i> Posting<'i, plain::Ident> {
200 pub fn new_untracked<T>(account: T) -> Self
201 where
202 T: Into<Cow<'i, str>>,
203 {
204 Posting {
205 account: account.into(),
206 clear_state: ClearState::default(),
207 amount: None,
208 balance: None,
209 metadata: Vec::new(),
210 }
211 }
212}
213
214impl<'i, Deco: Decoration> Posting<'i, Deco> {
215 pub fn new(account: Deco::Decorated<Cow<'i, str>>) -> Self {
216 Posting {
217 account,
218 clear_state: ClearState::default(),
219 amount: None,
220 balance: None,
221 metadata: Vec::new(),
222 }
223 }
224}
225
226#[derive(Debug, PartialEq, Eq, Clone, Copy, Default, ToStatic)]
228pub enum ClearState {
229 #[default]
231 Uncleared,
232 Cleared,
234 Pending,
236}
237
238#[derive(Debug, PartialEq, Eq, ToStatic)]
240pub enum Metadata<'i> {
241 Comment(Cow<'i, str>),
243 WordTags(Vec<Cow<'i, str>>),
245 KeyValueTag {
247 key: Cow<'i, str>,
248 value: MetadataValue<'i>,
249 },
250}
251
252#[derive(Debug, PartialEq, Eq, ToStatic)]
254pub enum MetadataValue<'i> {
255 Text(Cow<'i, str>),
257 Expr(Cow<'i, str>),
260}
261
262#[derive_where(Debug, PartialEq, Eq)]
268pub struct PostingAmount<'i, Deco: Decoration> {
269 pub amount: Deco::Decorated<expr::ValueExpr<'i>>,
270 pub cost: Option<Deco::Decorated<Exchange<'i>>>,
271 pub lot: Lot<'i, Deco>,
272}
273
274impl PostingAmount<'_, plain::Ident> {
275 #[cfg(test)]
276 fn to_static(&self) -> PostingAmount<'static, plain::Ident> {
277 PostingAmount {
278 amount: self.amount.to_static(),
279 cost: self.cost.to_static(),
280 lot: self.lot.to_static(),
281 }
282 }
283}
284
285impl<'i> From<expr::ValueExpr<'i>> for PostingAmount<'i, plain::Ident> {
286 fn from(v: expr::ValueExpr<'i>) -> Self {
287 PostingAmount {
288 amount: v,
289 cost: None,
290 lot: Lot::default(),
291 }
292 }
293}
294
295#[derive_where(Debug, PartialEq, Eq)]
297pub struct Lot<'i, Deco: Decoration> {
298 pub price: Option<Deco::Decorated<Exchange<'i>>>,
299 pub date: Option<NaiveDate>,
300 pub note: Option<Cow<'i, str>>,
301}
302
303impl Lot<'_, plain::Ident> {
304 #[cfg(test)]
305 fn to_static(&self) -> Lot<'static, plain::Ident> {
306 Lot {
307 price: self.price.to_static(),
308 date: self.date.to_static(),
309 note: self.note.to_static(),
310 }
311 }
312}
313
314impl<Deco: Decoration> Default for Lot<'_, Deco> {
315 fn default() -> Self {
316 Self {
317 price: None,
318 date: None,
319 note: None,
320 }
321 }
322}
323
324#[derive(Debug, PartialEq, Eq, ToStatic)]
326pub enum Exchange<'i> {
327 Total(expr::ValueExpr<'i>),
332 Rate(expr::ValueExpr<'i>),
337}
338
339#[derive(Debug, PartialEq, Eq, ToStatic)]
342pub struct PriceDBEntry<'i> {
343 pub datetime: NaiveDateTime,
344 pub target: Cow<'i, str>,
346 pub rate: expr::Amount<'i>,
349}