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(Debug, PartialEq, Eq, Clone, Copy)]
22pub enum Separation {
23 Immediate,
25 BlankLine,
27}
28
29#[derive_where(Debug, PartialEq, Eq)]
31pub struct LedgerEntry<'i, Deco: Decoration> {
32 pub separation: Separation,
34 pub statement: LedgerStatement<'i, Deco>,
36}
37
38impl<'i, Deco: Decoration> LedgerEntry<'i, Deco> {
39 pub fn new(statement: LedgerStatement<'i, Deco>) -> Self {
42 Self {
43 separation: Separation::BlankLine,
44 statement,
45 }
46 }
47
48 pub fn immediate(self) -> Self {
50 Self {
51 separation: Separation::Immediate,
52 ..self
53 }
54 }
55}
56
57impl LedgerEntry<'_, plain::Ident> {
58 #[cfg(test)]
59 pub(crate) fn to_static(&self) -> LedgerEntry<'static, plain::Ident> {
60 LedgerEntry {
61 separation: self.separation,
62 statement: self.statement.to_static(),
63 }
64 }
65}
66
67#[derive_where(Debug, PartialEq, Eq)]
69pub enum LedgerStatement<'i, Deco: Decoration> {
70 Txn(Transaction<'i, Deco>),
72 Comment(TopLevelComment<'i>),
74 ApplyTag(ApplyTag<'i>),
76 EndApplyTag,
78 Include(IncludeFile<'i>),
80 Account(AccountDeclaration<'i>),
82 Commodity(CommodityDeclaration<'i>),
84}
85
86impl LedgerStatement<'_, plain::Ident> {
87 #[cfg(test)]
88 pub(crate) fn to_static(&self) -> LedgerStatement<'static, plain::Ident> {
89 match self {
90 LedgerStatement::Txn(v) => LedgerStatement::Txn(v.to_static()),
91 LedgerStatement::Comment(v) => LedgerStatement::Comment(v.to_static()),
92 LedgerStatement::ApplyTag(v) => LedgerStatement::ApplyTag(v.to_static()),
93 LedgerStatement::EndApplyTag => LedgerStatement::EndApplyTag,
94 LedgerStatement::Include(v) => LedgerStatement::Include(v.to_static()),
95 LedgerStatement::Account(v) => LedgerStatement::Account(v.to_static()),
96 LedgerStatement::Commodity(v) => LedgerStatement::Commodity(v.to_static()),
97 }
98 }
99}
100
101#[derive(Debug, PartialEq, Eq, ToStatic)]
103pub struct TopLevelComment<'i>(pub Cow<'i, str>);
104
105#[derive(Debug, PartialEq, Eq, ToStatic)]
107pub struct ApplyTag<'i> {
108 pub key: Cow<'i, str>,
109 pub value: Option<MetadataValue<'i>>,
110}
111
112#[derive(Debug, PartialEq, Eq, ToStatic)]
115pub struct IncludeFile<'i>(pub Cow<'i, str>);
116
117#[derive(Debug, PartialEq, Eq, ToStatic)]
119pub struct AccountDeclaration<'i> {
120 pub name: Cow<'i, str>,
122 pub details: Vec<AccountDetail<'i>>,
124}
125
126#[derive(Debug, PartialEq, Eq, ToStatic)]
128pub enum AccountDetail<'i> {
129 Comment(Cow<'i, str>),
131 Note(Cow<'i, str>),
134 Alias(Cow<'i, str>),
136}
137
138#[derive(Debug, PartialEq, Eq, ToStatic)]
140pub struct CommodityDeclaration<'i> {
141 pub name: Cow<'i, str>,
143 pub details: Vec<CommodityDetail<'i>>,
145}
146
147#[derive(Debug, PartialEq, Eq, ToStatic)]
149pub enum CommodityDetail<'i> {
150 Comment(Cow<'i, str>),
152 Note(Cow<'i, str>),
155 Alias(Cow<'i, str>),
158 Format(expr::Amount<'i>),
160}
161
162#[derive_where(Debug, PartialEq, Eq)]
164pub struct Transaction<'i, Deco: Decoration> {
165 pub date: NaiveDate,
167 pub effective_date: Option<NaiveDate>,
169 pub clear_state: ClearState,
171 pub code: Option<Cow<'i, str>>,
173 pub payee: Cow<'i, str>,
175 pub posts: Vec<Deco::Decorated<Posting<'i, Deco>>>,
177 pub metadata: Vec<Metadata<'i>>,
179}
180
181impl Transaction<'_, plain::Ident> {
182 #[cfg(test)]
183 fn to_static(&self) -> Transaction<'static, plain::Ident> {
184 let mut posts = Vec::new();
185 for p in &self.posts {
186 posts.push(p.to_static());
187 }
188 Transaction {
189 date: self.date,
190 effective_date: self.effective_date,
191 clear_state: self.clear_state,
192 code: self.code.to_static(),
193 payee: self.payee.to_static(),
194 posts,
195 metadata: self.metadata.to_static(),
196 }
197 }
198}
199
200impl<'i, Deco: Decoration> Transaction<'i, Deco> {
201 pub fn new<T>(date: NaiveDate, payee: T) -> Self
203 where
204 T: Into<Cow<'i, str>>,
205 {
206 Transaction {
207 date,
208 effective_date: None,
209 clear_state: ClearState::Uncleared,
210 code: None,
211 payee: payee.into(),
212 metadata: Vec::new(),
213 posts: Vec::new(),
214 }
215 }
216}
217
218#[derive_where(Debug, PartialEq, Eq)]
219pub struct Posting<'i, Deco: Decoration> {
221 pub account: Deco::Decorated<Cow<'i, str>>,
223 pub clear_state: ClearState,
225 pub amount: Option<PostingAmount<'i, Deco>>,
227 pub balance: Option<Deco::Decorated<expr::ValueExpr<'i>>>,
229 pub metadata: Vec<Metadata<'i>>,
231}
232
233impl Posting<'_, plain::Ident> {
234 #[cfg(test)]
235 fn to_static(&self) -> Posting<'static, plain::Ident> {
236 Posting {
237 account: self.account.to_static(),
238 clear_state: self.clear_state,
239 amount: self.amount.as_ref().map(|x| x.to_static()),
240 balance: self.balance.to_static(),
241 metadata: self.metadata.to_static(),
242 }
243 }
244}
245
246impl<'i> Posting<'i, plain::Ident> {
247 pub fn new_untracked<T>(account: T) -> Self
248 where
249 T: Into<Cow<'i, str>>,
250 {
251 Posting {
252 account: account.into(),
253 clear_state: ClearState::default(),
254 amount: None,
255 balance: None,
256 metadata: Vec::new(),
257 }
258 }
259}
260
261impl<'i, Deco: Decoration> Posting<'i, Deco> {
262 pub fn new(account: Deco::Decorated<Cow<'i, str>>) -> Self {
263 Posting {
264 account,
265 clear_state: ClearState::default(),
266 amount: None,
267 balance: None,
268 metadata: Vec::new(),
269 }
270 }
271}
272
273#[derive(Debug, PartialEq, Eq, Clone, Copy, Default, ToStatic)]
275pub enum ClearState {
276 #[default]
278 Uncleared,
279 Cleared,
281 Pending,
283}
284
285#[derive(Debug, PartialEq, Eq, ToStatic)]
287pub enum Metadata<'i> {
288 Comment(Cow<'i, str>),
292 WordTags(Vec<Cow<'i, str>>),
294 KeyValueTag {
296 key: Cow<'i, str>,
297 value: MetadataValue<'i>,
298 },
299}
300
301#[derive(Debug, PartialEq, Eq, ToStatic)]
303pub enum MetadataValue<'i> {
304 Text(Cow<'i, str>),
306 Expr(Cow<'i, str>),
309}
310
311#[derive_where(Debug, PartialEq, Eq)]
317pub struct PostingAmount<'i, Deco: Decoration> {
318 pub amount: Deco::Decorated<expr::ValueExpr<'i>>,
319 pub cost: Option<Deco::Decorated<Exchange<'i>>>,
320 pub lot: Lot<'i, Deco>,
321}
322
323impl PostingAmount<'_, plain::Ident> {
324 #[cfg(test)]
325 fn to_static(&self) -> PostingAmount<'static, plain::Ident> {
326 PostingAmount {
327 amount: self.amount.to_static(),
328 cost: self.cost.to_static(),
329 lot: self.lot.to_static(),
330 }
331 }
332}
333
334impl<'i> From<expr::ValueExpr<'i>> for PostingAmount<'i, plain::Ident> {
335 fn from(v: expr::ValueExpr<'i>) -> Self {
336 PostingAmount {
337 amount: v,
338 cost: None,
339 lot: Lot::default(),
340 }
341 }
342}
343
344#[derive_where(Debug, PartialEq, Eq)]
346pub struct Lot<'i, Deco: Decoration> {
347 pub price: Option<Deco::Decorated<Exchange<'i>>>,
348 pub date: Option<NaiveDate>,
349 pub note: Option<Cow<'i, str>>,
350}
351
352impl Lot<'_, plain::Ident> {
353 #[cfg(test)]
354 fn to_static(&self) -> Lot<'static, plain::Ident> {
355 Lot {
356 price: self.price.to_static(),
357 date: self.date.to_static(),
358 note: self.note.to_static(),
359 }
360 }
361}
362
363impl<Deco: Decoration> Default for Lot<'_, Deco> {
364 fn default() -> Self {
365 Self {
366 price: None,
367 date: None,
368 note: None,
369 }
370 }
371}
372
373#[derive(Debug, PartialEq, Eq, ToStatic)]
375pub enum Exchange<'i> {
376 Total(expr::ValueExpr<'i>),
381 Rate(expr::ValueExpr<'i>),
386}
387
388#[derive(Debug, PartialEq, Eq, ToStatic)]
391pub struct PriceDBEntry<'i> {
392 pub datetime: NaiveDateTime,
393 pub target: Cow<'i, str>,
395 pub rate: expr::Amount<'i>,
398}