Skip to main content

Spend

Struct Spend 

Source
pub struct Spend {
    pub selected: Vec<ContextEntry>,
    pub excluded: Vec<Excluded>,
    pub deduplicated: Vec<Deduplicated>,
    pub conflicts: Vec<Conflict>,
    pub budget: Budget,
}
Expand description

Everything the budget decided, as four lists and the arithmetic behind them: what the session is given, what it is not and why, what collapsed into what, and what disagrees with what.

The four lists are disjoint views of one walk rather than four separate answers, and the one invariant that joins them to the budget is that budget.used_tokens is exactly the sum of selected’s costs — nothing is spent that is not in the answer, and nothing is in the answer that was not spent. An under-filled context is debugged from excluded, not by guessing, which is why it carries a reason and a cost per row.

Every field is populated on every call, including with nothing reached: the per-tier spend always has all six tiers, so a reader never has to distinguish “this tier was empty” from “this tier is missing from the answer”.

use std::collections::{BTreeMap, BTreeSet};

use majordomus_cli::devcontext::budget::{spend, Spend};
use majordomus_cli::devcontext::select::Selection;
use majordomus_cli::devcontext::{Tier, DEFAULT_BUDGET_TOKENS};
use majordomus_cli::model::Diagnostic;
use majordomus_cli::synthetic::SyntheticRepository;

let repo = SyntheticRepository::small().unwrap();
let index = repo.index().unwrap();
let nothing = Selection {
    candidates: BTreeMap::new(),
    unresolved: Vec::new(),
    diagnostics: Vec::new(),
};

// the answer and what could not be read, side by side and neither an error
let (s, diagnostics): (Spend, Vec<Diagnostic>) = spend(
    &index, nothing, &BTreeSet::new(), DEFAULT_BUDGET_TOKENS, &BTreeMap::new(),
);

// reaching nothing is an answer, not an error, and it is a complete one
assert!(s.selected.is_empty() && s.excluded.is_empty());
assert!(s.deduplicated.is_empty() && s.conflicts.is_empty());
assert!(diagnostics.is_empty());

// the per-tier spend is always total, so no tier is merely absent
let tiers: Vec<Tier> = s.budget.tiers.iter().map(|t| t.tier).collect();
assert_eq!(tiers, Tier::ORDER.to_vec());

// and the invariant that ties the lists to the arithmetic
let spent: u64 = s.selected.iter().map(|e| e.cost_tokens).sum();
assert_eq!(s.budget.used_tokens, spent);
assert_eq!(s.budget.remaining_tokens, DEFAULT_BUDGET_TOKENS);

Fields§

§selected: Vec<ContextEntry>

What to give the session, in the order it was spent.

§excluded: Vec<Excluded>

What was reached and not given.

§deduplicated: Vec<Deduplicated>

What collapsed into what.

§conflicts: Vec<Conflict>

What does not agree with what.

§budget: Budget

The ceiling and the per-tier spend.

Auto Trait Implementations§

§

impl Freeze for Spend

§

impl RefUnwindSafe for Spend

§

impl Send for Spend

§

impl Sync for Spend

§

impl Unpin for Spend

§

impl UnsafeUnpin for Spend

§

impl UnwindSafe for Spend

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more