use time::Date;
use super::holds::HoldEffect;
use super::state::{BalanceKey, Window};
use crate::error::Rejection;
use crate::model::{
AccountHandle, AccountRegistration, Amount, Balance, BalanceLimit, Currency, Dimensions, Entry,
EntryId, Fingerprint, IdempotencyKey, Label, Layer, LedgerPolicy, MemoSalt, PeriodId,
PostingRef, Record, Seal, Side,
};
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Outcome<const P: u8> {
Append(Box<WriteSet<P>>),
Unchanged(Done<P>),
Batch {
writes: Vec<WriteSet<P>>,
done: Done<P>,
},
}
impl<const P: u8> Outcome<P> {
#[must_use]
pub const fn done(&self) -> &Done<P> {
match self {
Self::Append(write) => &write.done,
Self::Unchanged(done) | Self::Batch { done, .. } => done,
}
}
}
impl<const P: u8> From<WriteSet<P>> for Outcome<P> {
fn from(write: WriteSet<P>) -> Self {
Self::Append(Box::new(write))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Done<const P: u8> {
Genesis,
Entry(Recorded),
Account(AccountHandle),
Changed,
Period(PeriodId),
Sealed(SealReceipt<P>),
Cleared(u64),
Closing(Vec<Entry<P>>),
Batch(Vec<Recorded>),
BatchRejected {
index: usize,
rejection: Rejection,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct Recorded {
pub id: EntryId,
pub position: u64,
pub replayed: bool,
}
impl core::fmt::Display for Recorded {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let replayed = if self.replayed { ", replayed" } else { "" };
let text = format!("entry {} at position {}{replayed}", self.id, self.position);
crate::model::pad(f, &text)
}
}
impl<const P: u8> core::fmt::Display for SealReceipt<P> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let text = format!("{} sealed at position {}", self.period, self.position);
crate::model::pad(f, &text)
}
}
impl Recorded {
pub(crate) const fn new(id: EntryId, position: u64, replayed: bool) -> Self {
Self {
id,
position,
replayed,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct SealReceipt<const P: u8 = 2> {
pub period: PeriodId,
pub position: u64,
pub seal: Seal<P>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct WriteSet<const P: u8> {
pub position: u64,
pub record: Record<P>,
pub leaf: Vec<u8>,
pub memo: Option<MemoPlain>,
pub done: Done<P>,
pub postings: Vec<PostingRow<P>>,
pub balances: Vec<BalanceDelta<P>>,
pub index: Vec<IndexRow<P>>,
pub hold: Option<HoldEffect<P>>,
pub outstanding: Vec<Outstanding<P>>,
}
impl<const P: u8> WriteSet<P> {
pub(crate) fn of(position: u64, record: Record<P>, done: Done<P>) -> Self {
let leaf = record.to_leaf_bytes();
Self {
position,
record,
leaf,
memo: None,
done,
postings: Vec::new(),
balances: Vec::new(),
index: Vec::new(),
hold: None,
outstanding: Vec::new(),
}
}
pub(crate) fn with(mut self, row: IndexRow<P>) -> Self {
self.index.push(row);
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Outstanding<const P: u8> {
pub handle: AccountHandle,
pub currency: Currency,
pub debits: Amount<P>,
pub credits: Amount<P>,
}
impl<const P: u8> Outstanding<P> {
#[must_use]
pub const fn is_zero(&self) -> bool {
self.debits.is_zero() && self.credits.is_zero()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MemoPlain {
pub salt: MemoSalt,
pub text: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct PostingRow<const P: u8> {
pub index: u16,
pub key: BalanceKey,
pub side: Side,
pub amount: Amount<P>,
pub booking: Date,
pub value: Date,
pub dimensions: Dimensions,
pub balance_after: Balance<P>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct BalanceDelta<const P: u8> {
pub key: BalanceKey,
pub delta: Balance<P>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum AccountTerm {
Window(Window),
Limit(BalanceLimit),
Class(Option<Label>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum IndexRow<const P: u8> {
EntryKey {
key: IdempotencyKey,
id: EntryId,
position: u64,
fingerprint: Option<Fingerprint>,
},
Reversal {
target: EntryId,
by: EntryId,
},
Account {
registration: AccountRegistration,
position: u64,
},
AccountVersion {
handle: AccountHandle,
term: AccountTerm,
effective: Date,
position: u64,
},
CurrencyVersion {
code: Currency,
exponent: u8,
effective: Date,
position: u64,
},
PolicyVersion {
policy: LedgerPolicy,
effective: Date,
position: u64,
},
Period {
id: PeriodId,
start: Date,
end: Date,
position: u64,
},
Sealed {
period: PeriodId,
position: u64,
},
SealTree {
period: PeriodId,
currency: Currency,
layer: Layer,
leaves: Vec<(AccountHandle, Balance<P>)>,
},
ClearingKey {
key: IdempotencyKey,
position: u64,
},
Applied {
posting: PostingRef,
delta: Amount<P>,
},
ClearingReset {
clearing: u64,
position: u64,
},
}