use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Fuel {
left: Option<u32>,
spent: u32,
}
impl Fuel {
#[must_use]
pub const fn unlimited() -> Self {
Self { left: None, spent: 0 }
}
#[must_use]
pub const fn of(count: u32) -> Self {
Self { left: Some(count), spent: 0 }
}
pub fn take(&mut self) -> bool {
match &mut self.left {
Some(0) => false,
Some(left) => {
*left -= 1;
self.spent += 1;
true
}
None => {
self.spent += 1;
true
}
}
}
#[must_use]
pub const fn spent(self) -> u32 {
self.spent
}
#[must_use]
pub const fn is_empty(self) -> bool {
matches!(self.left, Some(0))
}
}
impl Default for Fuel {
fn default() -> Self {
Self::unlimited()
}
}
impl fmt::Display for Fuel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.left {
None => write!(f, "{} of unlimited", self.spent),
Some(left) => write!(f, "{} of {}", self.spent, self.spent + left),
}
}
}
#[cfg(test)]
mod tests {
use super::Fuel;
#[test]
fn unlimited_fuel_is_never_refused_and_still_counts() {
let mut fuel = Fuel::unlimited();
for _ in 0..1000 {
assert!(fuel.take());
}
assert_eq!(fuel.spent(), 1000);
assert!(!fuel.is_empty());
}
#[test]
fn a_limit_of_three_allows_three_and_then_stops_allowing_any() {
let mut fuel = Fuel::of(3);
assert!(fuel.take());
assert!(fuel.take());
assert!(fuel.take());
assert!(!fuel.take());
assert!(!fuel.take());
assert_eq!(fuel.spent(), 3);
assert!(fuel.is_empty());
}
#[test]
fn a_limit_of_zero_allows_nothing_at_all() {
let mut fuel = Fuel::of(0);
assert!(!fuel.take());
assert_eq!(fuel.spent(), 0);
assert!(fuel.is_empty());
}
#[test]
fn the_display_says_how_much_of_how_much() {
let mut fuel = Fuel::of(4);
assert!(fuel.take());
assert_eq!(fuel.to_string(), "1 of 4");
let mut open = Fuel::unlimited();
assert!(open.take());
assert_eq!(open.to_string(), "1 of unlimited");
}
}