pub enum CostNumber {
PerUnit {
value: Decimal,
},
Total {
value: Decimal,
},
PerUnitFromTotal(BookedCost),
}Expand description
A cost specification for matching or creating costs.
Unlike Cost, all fields are optional to allow partial matching.
This is used in postings where the user may specify only some
cost components (e.g., just the date to match a specific lot).
§Matching Rules
A CostSpec matches a Cost if all specified fields match:
- If
numberisSome, it must equal the cost’s number - If
currencyisSome, it must equal the cost’s currency - If
dateisSome, it must equal the cost’s date - If
labelisSome, it must equal the cost’s label
§Examples
use rustledger_core::{Cost, CostSpec};
use rust_decimal_macros::dec;
let cost = Cost::new(dec!(150.00), "USD")
.with_date(rustledger_core::naive_date(2024, 1, 15).unwrap());
// Match by date only
let spec = CostSpec::default().with_date(rustledger_core::naive_date(2024, 1, 15).unwrap());
assert!(spec.matches(&cost));
// Match by wrong date
let spec2 = CostSpec::default().with_date(rustledger_core::naive_date(2024, 1, 16).unwrap());
assert!(!spec2.matches(&cost));The numeric component of a CostSpec.
Beancount cost specs name a number in one of two source-level shapes:
{150.00 USD}— per-unit cost (Self::PerUnit){{ 1500.00 USD }}— total cost for the posting’s units (Self::Total)
During booking the engine converts Total(t) into a third state,
Self::PerUnitFromTotal, carrying both the derived per-unit
value (for display, lot tracking) and the original total (for
precision-preserving residual math — division-then-multiplication
loses precision at the rust_decimal 28-digit ceiling).
A cost spec without a number at all (e.g. {} for a booking-
deferred lot match) is represented by CostSpec.number: None.
Pre-#1164 the per-unit and total numbers were two independent
Option<Decimal> fields on CostSpec. The invalid both-set state
was prevented only by parser discipline and downstream defensive
branches; the “booked from total” state was modeled accidentally
by setting both fields, with the meaning encoded only in code
comments. Folding the axes into one enum makes both the
pre-booking invalid state unrepresentable AND the post-booking
derived state explicit.
Variants§
PerUnit
Per-unit cost as written: {150.00 USD}. Booking leaves this
shape unchanged.
Total
Total cost as written: {{ 1500.00 USD }}. Booking rewrites
this to Self::PerUnitFromTotal once units are known.
PerUnitFromTotal(BookedCost)
Post-booking state: a per-unit value derived from a
{{ total USD }} spec at booking time, with the source total
preserved for exact residual math. Pre-#1164 this was modeled
implicitly by setting both number_per and number_total on
CostSpec. The payload is a separate BookedCost struct
so the booking-time invariant lives on a named type with
constructor methods that enforce it.
Implementations§
Source§impl CostNumber
impl CostNumber
Sourcepub const fn per_unit(&self) -> Option<Decimal>
pub const fn per_unit(&self) -> Option<Decimal>
Return the per-unit value if this number carries one.
Self::PerUnit→Some(its Decimal)Self::PerUnitFromTotal→Some(per_unit)Self::Total→None(booking hasn’t computed per-unit yet)
Sourcepub const fn total(&self) -> Option<Decimal>
pub const fn total(&self) -> Option<Decimal>
Return the total value if this number carries one.
Self::Total→Some(its Decimal)Self::PerUnitFromTotal→Some(total)Self::PerUnit→None
Trait Implementations§
Source§impl Archive for CostNumber
impl Archive for CostNumber
Source§type Archived = ArchivedCostNumber
type Archived = ArchivedCostNumber
Source§type Resolver = CostNumberResolver
type Resolver = CostNumberResolver
Source§fn resolve(
&self,
resolver: <Self as Archive>::Resolver,
out: Place<<Self as Archive>::Archived>,
)
fn resolve( &self, resolver: <Self as Archive>::Resolver, out: Place<<Self as Archive>::Archived>, )
Source§const COPY_OPTIMIZATION: CopyOptimization<Self> = _
const COPY_OPTIMIZATION: CopyOptimization<Self> = _
serialize. Read moreSource§impl Clone for CostNumber
impl Clone for CostNumber
Source§fn clone(&self) -> CostNumber
fn clone(&self) -> CostNumber
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreimpl Copy for CostNumber
Source§impl Debug for CostNumber
impl Debug for CostNumber
Source§impl<'de> Deserialize<'de> for CostNumber
impl<'de> Deserialize<'de> for CostNumber
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl<__D: Fallible + ?Sized> Deserialize<CostNumber, __D> for Archived<CostNumber>where
AsDecimal: ArchiveWith<Decimal> + DeserializeWith<<AsDecimal as ArchiveWith<Decimal>>::Archived, Decimal, __D>,
BookedCost: Archive,
<BookedCost as Archive>::Archived: Deserialize<BookedCost, __D>,
impl<__D: Fallible + ?Sized> Deserialize<CostNumber, __D> for Archived<CostNumber>where
AsDecimal: ArchiveWith<Decimal> + DeserializeWith<<AsDecimal as ArchiveWith<Decimal>>::Archived, Decimal, __D>,
BookedCost: Archive,
<BookedCost as Archive>::Archived: Deserialize<BookedCost, __D>,
Source§fn deserialize(
&self,
deserializer: &mut __D,
) -> Result<CostNumber, <__D as Fallible>::Error>
fn deserialize( &self, deserializer: &mut __D, ) -> Result<CostNumber, <__D as Fallible>::Error>
impl Eq for CostNumber
Source§impl Hash for CostNumber
impl Hash for CostNumber
Source§impl PartialEq for CostNumber
impl PartialEq for CostNumber
Source§fn eq(&self, other: &CostNumber) -> bool
fn eq(&self, other: &CostNumber) -> bool
self and other values to be equal, and is used by ==.Source§impl Serialize for CostNumber
impl Serialize for CostNumber
Source§impl ShiftSpans for CostNumber
impl ShiftSpans for CostNumber
impl StructuralPartialEq for CostNumber
Auto Trait Implementations§
impl Freeze for CostNumber
impl RefUnwindSafe for CostNumber
impl Send for CostNumber
impl Sync for CostNumber
impl Unpin for CostNumber
impl UnsafeUnpin for CostNumber
impl UnwindSafe for CostNumber
Blanket Implementations§
Source§impl<T> ArchivePointee for T
impl<T> ArchivePointee for T
Source§type ArchivedMetadata = ()
type ArchivedMetadata = ()
Source§fn pointer_metadata(
_: &<T as ArchivePointee>::ArchivedMetadata,
) -> <T as Pointee>::Metadata
fn pointer_metadata( _: &<T as ArchivePointee>::ArchivedMetadata, ) -> <T as Pointee>::Metadata
Source§impl<T> ArchiveUnsized for Twhere
T: Archive,
impl<T> ArchiveUnsized for Twhere
T: Archive,
Source§type Archived = <T as Archive>::Archived
type Archived = <T as Archive>::Archived
Archive, it may be
unsized. Read moreSource§fn archived_metadata(
&self,
) -> <<T as ArchiveUnsized>::Archived as ArchivePointee>::ArchivedMetadata
fn archived_metadata( &self, ) -> <<T as ArchiveUnsized>::Archived as ArchivePointee>::ArchivedMetadata
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<T> LayoutRaw for T
impl<T> LayoutRaw for T
Source§fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>
fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>
Source§impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
Source§unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool
unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool
Source§fn resolve_niched(out: Place<NichedOption<T, N1>>)
fn resolve_niched(out: Place<NichedOption<T, N1>>)
out indicating that a T is niched.