pub const MAINTENANCE_UNITS: u32 = 4096;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Budget {
left: u32,
spent: u32,
}
impl Budget {
#[must_use]
pub const fn new(units: u32) -> Budget {
Budget {
left: units,
spent: 0,
}
}
#[must_use]
pub const fn standard() -> Budget {
Budget::new(MAINTENANCE_UNITS)
}
#[must_use]
pub const fn none() -> Budget {
Budget::new(0)
}
#[inline]
pub const fn spend(&mut self, units: u32) -> bool {
self.spent = self.spent.saturating_add(units);
self.left = self.left.saturating_sub(units);
self.left > 0
}
#[must_use]
#[inline]
pub const fn left(&self) -> u32 {
self.left
}
#[must_use]
#[inline]
pub const fn spent(&self) -> u32 {
self.spent
}
#[must_use]
#[inline]
pub const fn is_spent(&self) -> bool {
self.left == 0
}
}
impl Default for Budget {
fn default() -> Budget {
Budget::standard()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_budget_runs_out_and_says_so() {
let mut b = Budget::new(10);
assert!(b.spend(4));
assert!(b.spend(5));
assert!(!b.spend(1), "the tenth unit is the last one");
assert!(b.is_spent());
assert_eq!(b.spent(), 10);
}
#[test]
fn overspending_is_allowed_and_recorded() {
let mut b = Budget::new(10);
assert!(
!b.spend(1000),
"one item can cost more than the whole slice"
);
assert_eq!(b.left(), 0);
assert_eq!(b.spent(), 1000, "what it cost, not what it was allowed");
}
#[test]
fn an_empty_budget_stops_before_the_first_item() {
let mut b = Budget::none();
assert!(b.is_spent());
assert!(!b.spend(1));
}
#[test]
fn spending_cannot_wrap() {
let mut b = Budget::new(u32::MAX);
assert!(b.spend(u32::MAX - 1));
assert!(!b.spend(u32::MAX));
assert_eq!(b.left(), 0);
assert_eq!(b.spent(), u32::MAX);
}
}