pub struct Inventory { /* private fields */ }Expand description
An inventory is a collection of positions.
It tracks all positions for an account and supports booking operations for adding and reducing positions.
§Examples
use rustledger_core::{Inventory, Position, Amount, Cost, BookingMethod};
use rust_decimal_macros::dec;
let mut inv = Inventory::new();
// Add a simple position
inv.add(Position::simple(Amount::new(dec!(100), "USD")));
assert_eq!(inv.units("USD"), dec!(100));
// Add a position with cost
let cost = Cost::new(dec!(150.00), "USD");
inv.add(Position::with_cost(Amount::new(dec!(10), "AAPL"), cost));
assert_eq!(inv.units("AAPL"), dec!(10));Implementations§
Source§impl Inventory
impl Inventory
Sourcepub fn try_reduce(
&self,
units: &Amount,
cost_spec: Option<&CostSpec>,
method: BookingMethod,
) -> Result<BookingResult, BookingError>
pub fn try_reduce( &self, units: &Amount, cost_spec: Option<&CostSpec>, method: BookingMethod, ) -> Result<BookingResult, BookingError>
Try reducing positions without modifying the inventory.
This is a read-only version of reduce() that returns what would be matched
without actually modifying the inventory. Useful for previewing booking results
before committing.
§Arguments
units- The units to reduce (negative for selling)cost_spec- Optional cost specification for matching lotsmethod- The booking method to use
§Returns
Returns a BookingResult with the positions that would be matched and cost basis,
or a BookingError if the reduction cannot be performed.
Source§impl Inventory
impl Inventory
Sourcepub fn positions(&self) -> impl Iterator<Item = &Position> + '_
pub fn positions(&self) -> impl Iterator<Item = &Position> + '_
Iterate over all positions.
Previously returned &[Position]; now returns an iterator
because the underlying storage is a tree-based persistent
vector (im::Vector) that doesn’t expose a contiguous slice.
Most callers already iterate — for callers that need
random-access / indexed / .len() slice semantics, see
Self::position_list.
Sourcepub fn position_list(&self) -> Vec<&Position>
pub fn position_list(&self) -> Vec<&Position>
Materialize all positions as a Vec<&Position> for slice-style
access (indexing, .len(), .first(), .is_empty()).
Allocates O(N) pointers per call. Callers that only iterate
once should use Self::positions instead — this is for code
paths that need slice semantics.
Sourcepub const fn positions_mut(&mut self) -> &mut Vector<Position>
pub const fn positions_mut(&mut self) -> &mut Vector<Position>
Get mutable access to the underlying positions vector.
Returns &mut im::Vector<Position> (was &mut Vec<Position>
before issue #1086). im::Vector supports the same surface
for push_back, pop_back, retain, indexed access, and
iteration — but mutations are O(log N) with structural sharing
instead of O(1) amortized.
Sourcepub fn units(&self, currency: &str) -> Decimal
pub fn units(&self, currency: &str) -> Decimal
Get total units of a currency (ignoring cost lots).
This sums all positions of the given currency regardless of cost basis. Uses an internal cache for O(1) lookups.
Sourcepub fn currencies(&self) -> Vec<&str>
pub fn currencies(&self) -> Vec<&str>
Get all currencies in this inventory.
Sourcepub fn is_reduced_by(&self, units: &Amount, scope: ReductionScope) -> bool
pub fn is_reduced_by(&self, units: &Amount, scope: ReductionScope) -> bool
Check if the given units would reduce (not augment) this inventory.
Returns true if there’s a position with the same currency but opposite
sign, meaning these units would reduce the inventory rather than add to it.
When has_cost_spec is true, only positions with a cost basis are
considered for reduction matching. Simple (no-cost) positions are ignored
because they live in a different “cost layer” — a sell-without-cost-spec
that left a negative simple position should not cause a subsequent
cost-bearing augmentation to be misclassified as a reduction.
See: issue #875, beancount#889.
This is used to determine whether a posting is a sale/reduction or a purchase/augmentation.
Sourcepub fn book_value(
&self,
units_currency: &str,
) -> FxHashMap<InternedStr, Decimal>
pub fn book_value( &self, units_currency: &str, ) -> FxHashMap<InternedStr, Decimal>
Get the total book value (cost basis) for a currency.
Returns the sum of all cost bases for positions of the given currency.
Sourcepub fn add(&mut self, position: Position)
pub fn add(&mut self, position: Position)
Add a position to the inventory.
For positions without cost, this merges with existing positions
of the same currency using O(1) HashMap lookup.
For positions with cost, this adds as a new lot (O(1)). Lot aggregation for display purposes is handled separately at output time (e.g., in the query result formatter).
§TLA+ Specification
Implements AddAmount action from Conservation.tla:
- Invariant:
inventory + totalReduced = totalAdded - After add:
totalAdded' = totalAdded + amount
See: spec/tla/Conservation.tla
Sourcepub fn reduce(
&mut self,
units: &Amount,
cost_spec: Option<&CostSpec>,
method: BookingMethod,
) -> Result<BookingResult, BookingError>
pub fn reduce( &mut self, units: &Amount, cost_spec: Option<&CostSpec>, method: BookingMethod, ) -> Result<BookingResult, BookingError>
Reduce positions from the inventory using the specified booking method.
§Arguments
units- The units to reduce (negative for selling)cost_spec- Optional cost specification for matching lotsmethod- The booking method to use
§Returns
Returns a BookingResult with the matched positions and cost basis,
or a BookingError if the reduction cannot be performed.
§TLA+ Specification
Implements ReduceAmount action from Conservation.tla:
- Invariant:
inventory + totalReduced = totalAdded - After reduce:
totalReduced' = totalReduced + amount - Precondition:
amount <= inventory(elseInsufficientUnitserror)
Lot selection follows these TLA+ specs based on method:
Fifo:FIFOCorrect.tla- Oldest lots first (selected_date <= all other dates)Lifo:LIFOCorrect.tla- Newest lots first (selected_date >= all other dates)Hifo:HIFOCorrect.tla- Highest cost first (selected_cost >= all other costs)
See: spec/tla/Conservation.tla, spec/tla/FIFOCorrect.tla, etc.
Trait Implementations§
Source§impl<'de> Deserialize<'de> for Inventory
impl<'de> Deserialize<'de> for Inventory
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 FromIterator<Position> for Inventory
impl FromIterator<Position> for Inventory
impl Eq for Inventory
Auto Trait Implementations§
impl Freeze for Inventory
impl RefUnwindSafe for Inventory
impl Send for Inventory
impl Sync for Inventory
impl Unpin for Inventory
impl UnsafeUnpin for Inventory
impl UnwindSafe for Inventory
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> 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,
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.