pub struct CostSpec {
pub number: Option<CostNumber>,
pub currency: Option<Currency>,
pub date: Option<NaiveDate>,
pub label: Option<String>,
pub merge: bool,
}Expand description
A cost specification on a posting ({...} or {{...}}).
Carries the parsed cost-spec axes: the numeric component (per-unit
vs total, modeled as the mutually-exclusive CostNumber enum),
currency, lot date, label, and merge flag. Any subset may be
missing — {} corresponds to all-fields-None plus merge: false,
which lets the booker do lot matching deferred to inventory.
Pre-#1164 this struct had two independent Option<Decimal> fields
(number_per, number_total). The mutual-exclusion invariant was
enforced only by parser discipline; the post-booking “derived per-
unit from total” state was modeled accidentally by setting both
fields at once. The new shape (number: Option<CostNumber>) makes
the invalid state unrepresentable and the derived state explicit.
Fields§
§number: Option<CostNumber>The numeric component: per-unit, total, or absent.
Replaces the pre-#1164 number_per / number_total pair, which
allowed the invalid both-set state at the type level. See
CostNumber for the per-unit vs total distinction.
currency: Option<Currency>Currency of the cost (if specified)
date: Option<NaiveDate>Acquisition date (if specified)
label: Option<String>Lot label (if specified)
merge: boolWhether to merge with existing lot (average cost)
Implementations§
Source§impl CostSpec
impl CostSpec
Sourcepub const fn with_number(self, number: CostNumber) -> Self
pub const fn with_number(self, number: CostNumber) -> Self
Set the cost number directly.
The mutual exclusion between per-unit and total is enforced by
the CostNumber enum — there is no way to set both. Callers
construct the variant explicitly:
CostSpec::empty().with_number(CostNumber::PerUnit { value: dec!(150) });
CostSpec::empty().with_number(CostNumber::Total { value: dec!(1500) });Pre-#1164 this slot was a pair of Option<Decimal> fields;
pre-this-PR there were with_per_unit / with_total
convenience shims that perpetuated the two-axis mental model
in caller code and silently overwrote each other if both were
called. Both are gone — there’s exactly one way to set a cost
number now.
Sourcepub fn with_currency(self, currency: impl Into<Currency>) -> Self
pub fn with_currency(self, currency: impl Into<Currency>) -> Self
Set the currency.
Sourcepub fn with_label(self, label: impl Into<String>) -> Self
pub fn with_label(self, label: impl Into<String>) -> Self
Set the label.
Sourcepub const fn with_merge(self) -> Self
pub const fn with_merge(self) -> Self
Set the merge flag (for average cost booking).
Sourcepub fn matches(&self, cost: &Cost) -> bool
pub fn matches(&self, cost: &Cost) -> bool
Check if this cost spec matches a cost.
All specified fields must match the corresponding cost fields.
Per-unit matching uses CostNumber::per_unit() — a Total-only
spec doesn’t constrain the per-unit lot value (booking hasn’t
resolved it yet), but a PerUnitFromTotal post-booking spec
does.
Sourcepub fn resolve(&self, units: Decimal, date: NaiveDate) -> Option<Cost>
pub fn resolve(&self, units: Decimal, date: NaiveDate) -> Option<Cost>
Resolve this cost spec to a concrete cost, given the number of units.
If the number is CostNumber::Total, the per-unit cost is
calculated as total / |units|. Full precision is preserved to
avoid cost basis errors when the position is later sold.
PerUnitFromTotal already carries the derived per-unit value
from a prior booking pass — using b.per_unit directly is
equivalent to recomputing b.total / |units| because
BookedCost::new enforces that invariant at construction.
Returns None if required fields (currency, number) are missing.
Trait Implementations§
Source§impl Archive for CostSpec
impl Archive for CostSpec
Source§type Archived = ArchivedCostSpec
type Archived = ArchivedCostSpec
Source§type Resolver = CostSpecResolver
type Resolver = CostSpecResolver
Source§fn resolve(&self, resolver: Self::Resolver, out: Place<Self::Archived>)
fn resolve(&self, resolver: Self::Resolver, out: Place<Self::Archived>)
Source§const COPY_OPTIMIZATION: CopyOptimization<Self> = _
const COPY_OPTIMIZATION: CopyOptimization<Self> = _
serialize. Read moreSource§impl<'de> Deserialize<'de> for CostSpec
impl<'de> Deserialize<'de> for CostSpec
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<CostSpec, __D> for Archived<CostSpec>where
Option<CostNumber>: Archive,
<Option<CostNumber> as Archive>::Archived: Deserialize<Option<CostNumber>, __D>,
Option<Currency>: Archive,
<Option<Currency> as Archive>::Archived: Deserialize<Option<Currency>, __D>,
Map<AsNaiveDate>: ArchiveWith<Option<NaiveDate>> + DeserializeWith<<Map<AsNaiveDate> as ArchiveWith<Option<NaiveDate>>>::Archived, Option<NaiveDate>, __D>,
Option<String>: Archive,
<Option<String> as Archive>::Archived: Deserialize<Option<String>, __D>,
bool: Archive,
<bool as Archive>::Archived: Deserialize<bool, __D>,
impl<__D: Fallible + ?Sized> Deserialize<CostSpec, __D> for Archived<CostSpec>where
Option<CostNumber>: Archive,
<Option<CostNumber> as Archive>::Archived: Deserialize<Option<CostNumber>, __D>,
Option<Currency>: Archive,
<Option<Currency> as Archive>::Archived: Deserialize<Option<Currency>, __D>,
Map<AsNaiveDate>: ArchiveWith<Option<NaiveDate>> + DeserializeWith<<Map<AsNaiveDate> as ArchiveWith<Option<NaiveDate>>>::Archived, Option<NaiveDate>, __D>,
Option<String>: Archive,
<Option<String> as Archive>::Archived: Deserialize<Option<String>, __D>,
bool: Archive,
<bool as Archive>::Archived: Deserialize<bool, __D>,
impl Eq for CostSpec
Source§impl ShiftSpans for CostSpec
impl ShiftSpans for CostSpec
impl StructuralPartialEq for CostSpec
Auto Trait Implementations§
impl Freeze for CostSpec
impl RefUnwindSafe for CostSpec
impl Send for CostSpec
impl Sync for CostSpec
impl Unpin for CostSpec
impl UnsafeUnpin for CostSpec
impl UnwindSafe for CostSpec
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.