pub mod decoration;
pub mod display;
pub mod expr;
pub mod plain;
pub mod tracked;
use std::{borrow::Cow, fmt};
use bounded_static::ToStatic;
use chrono::{NaiveDate, NaiveDateTime};
use derive_where::derive_where;
#[cfg(test)]
use bounded_static::ToBoundedStatic;
use decoration::Decoration;
#[derive_where(Debug, PartialEq, Eq)]
pub enum LedgerEntry<'i, Deco: Decoration> {
Txn(Transaction<'i, Deco>),
Comment(TopLevelComment<'i>),
ApplyTag(ApplyTag<'i>),
EndApplyTag,
Include(IncludeFile<'i>),
Account(AccountDeclaration<'i>),
Commodity(CommodityDeclaration<'i>),
}
impl LedgerEntry<'_, plain::Ident> {
#[cfg(test)]
pub(crate) fn to_static(&self) -> LedgerEntry<'static, plain::Ident> {
match self {
LedgerEntry::Txn(v) => LedgerEntry::Txn(v.to_static()),
LedgerEntry::Comment(v) => LedgerEntry::Comment(v.to_static()),
LedgerEntry::ApplyTag(v) => LedgerEntry::ApplyTag(v.to_static()),
LedgerEntry::EndApplyTag => LedgerEntry::EndApplyTag,
LedgerEntry::Include(v) => LedgerEntry::Include(v.to_static()),
LedgerEntry::Account(v) => LedgerEntry::Account(v.to_static()),
LedgerEntry::Commodity(v) => LedgerEntry::Commodity(v.to_static()),
}
}
}
#[derive(Debug, PartialEq, Eq, ToStatic)]
pub struct TopLevelComment<'i>(pub Cow<'i, str>);
#[derive(Debug, PartialEq, Eq, ToStatic)]
pub struct ApplyTag<'i> {
pub key: Cow<'i, str>,
pub value: Option<MetadataValue<'i>>,
}
#[derive(Debug, PartialEq, Eq, ToStatic)]
pub struct IncludeFile<'i>(pub Cow<'i, str>);
#[derive(Debug, PartialEq, Eq, ToStatic)]
pub struct AccountDeclaration<'i> {
pub name: Cow<'i, str>,
pub details: Vec<AccountDetail<'i>>,
}
#[derive(Debug, PartialEq, Eq, ToStatic)]
pub enum AccountDetail<'i> {
Comment(Cow<'i, str>),
Note(Cow<'i, str>),
Alias(Cow<'i, str>),
}
#[derive(Debug, PartialEq, Eq, ToStatic)]
pub struct CommodityDeclaration<'i> {
pub name: Cow<'i, str>,
pub details: Vec<CommodityDetail<'i>>,
}
#[derive(Debug, PartialEq, Eq, ToStatic)]
pub enum CommodityDetail<'i> {
Comment(Cow<'i, str>),
Note(Cow<'i, str>),
Alias(Cow<'i, str>),
Format(expr::Amount<'i>),
}
#[derive_where(Debug, PartialEq, Eq)]
pub struct Transaction<'i, Deco: Decoration> {
pub date: NaiveDate,
pub effective_date: Option<NaiveDate>,
pub clear_state: ClearState,
pub code: Option<Cow<'i, str>>,
pub payee: Cow<'i, str>,
pub posts: Vec<Deco::Decorated<Posting<'i, Deco>>>,
pub metadata: Vec<Metadata<'i>>,
}
impl Transaction<'_, plain::Ident> {
#[cfg(test)]
fn to_static(&self) -> Transaction<'static, plain::Ident> {
let mut posts = Vec::new();
for p in &self.posts {
posts.push(p.to_static());
}
Transaction {
date: self.date,
effective_date: self.effective_date,
clear_state: self.clear_state,
code: self.code.to_static(),
payee: self.payee.to_static(),
posts,
metadata: self.metadata.to_static(),
}
}
}
impl<'i, Deco: Decoration> Transaction<'i, Deco> {
pub fn new<T>(date: NaiveDate, payee: T) -> Self
where
T: Into<Cow<'i, str>>,
{
Transaction {
date,
effective_date: None,
clear_state: ClearState::Uncleared,
code: None,
payee: payee.into(),
metadata: Vec::new(),
posts: Vec::new(),
}
}
}
#[derive_where(Debug, PartialEq, Eq)]
pub struct Posting<'i, Deco: Decoration> {
pub account: Deco::Decorated<Cow<'i, str>>,
pub clear_state: ClearState,
pub amount: Option<PostingAmount<'i, Deco>>,
pub balance: Option<Deco::Decorated<expr::ValueExpr<'i>>>,
pub metadata: Vec<Metadata<'i>>,
}
impl Posting<'_, plain::Ident> {
#[cfg(test)]
fn to_static(&self) -> Posting<'static, plain::Ident> {
Posting {
account: self.account.to_static(),
clear_state: self.clear_state,
amount: self.amount.as_ref().map(|x| x.to_static()),
balance: self.balance.to_static(),
metadata: self.metadata.to_static(),
}
}
}
impl<'i> Posting<'i, plain::Ident> {
pub fn new_untracked<T>(account: T) -> Self
where
T: Into<Cow<'i, str>>,
{
Posting {
account: account.into(),
clear_state: ClearState::default(),
amount: None,
balance: None,
metadata: Vec::new(),
}
}
}
impl<'i, Deco: Decoration> Posting<'i, Deco> {
pub fn new(account: Deco::Decorated<Cow<'i, str>>) -> Self {
Posting {
account,
clear_state: ClearState::default(),
amount: None,
balance: None,
metadata: Vec::new(),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default, ToStatic)]
pub enum ClearState {
#[default]
Uncleared,
Cleared,
Pending,
}
#[derive(Debug, PartialEq, Eq, ToStatic)]
pub enum Metadata<'i> {
Comment(Cow<'i, str>),
WordTags(Vec<Cow<'i, str>>),
KeyValueTag {
key: Cow<'i, str>,
value: MetadataValue<'i>,
},
}
#[derive(Debug, PartialEq, Eq, ToStatic)]
pub enum MetadataValue<'i> {
Text(Cow<'i, str>),
Expr(Cow<'i, str>),
}
#[derive_where(Debug, PartialEq, Eq)]
pub struct PostingAmount<'i, Deco: Decoration> {
pub amount: Deco::Decorated<expr::ValueExpr<'i>>,
pub cost: Option<Deco::Decorated<Exchange<'i>>>,
pub lot: Lot<'i, Deco>,
}
impl PostingAmount<'_, plain::Ident> {
#[cfg(test)]
fn to_static(&self) -> PostingAmount<'static, plain::Ident> {
PostingAmount {
amount: self.amount.to_static(),
cost: self.cost.to_static(),
lot: self.lot.to_static(),
}
}
}
impl<'i> From<expr::ValueExpr<'i>> for PostingAmount<'i, plain::Ident> {
fn from(v: expr::ValueExpr<'i>) -> Self {
PostingAmount {
amount: v,
cost: None,
lot: Lot::default(),
}
}
}
#[derive_where(Debug, PartialEq, Eq)]
pub struct Lot<'i, Deco: Decoration> {
pub price: Option<Deco::Decorated<Exchange<'i>>>,
pub date: Option<NaiveDate>,
pub note: Option<Cow<'i, str>>,
}
impl Lot<'_, plain::Ident> {
#[cfg(test)]
fn to_static(&self) -> Lot<'static, plain::Ident> {
Lot {
price: self.price.to_static(),
date: self.date.to_static(),
note: self.note.to_static(),
}
}
}
impl<Deco: Decoration> Default for Lot<'_, Deco> {
fn default() -> Self {
Self {
price: None,
date: None,
note: None,
}
}
}
#[derive(Debug, PartialEq, Eq, ToStatic)]
pub enum Exchange<'i> {
Total(expr::ValueExpr<'i>),
Rate(expr::ValueExpr<'i>),
}
#[derive(Debug, PartialEq, Eq, ToStatic)]
pub struct PriceDBEntry<'i> {
pub datetime: NaiveDateTime,
pub target: Cow<'i, str>,
pub rate: expr::Amount<'i>,
}