use clock::{Clock, FakeClock};
use std::{
collections::HashMap,
time::{Duration, SystemTime},
};
#[derive(Debug, Clone, thiserror::Error)]
pub enum PunchError {
#[error("employee: {} has already punched in", .0)]
AlreadyPunchedIn(String),
#[error("employee: {} is not punched in", .0)]
NotPunchedIn(String),
#[error("invalid punch out time")]
InvalidPunchOutTime,
}
#[derive(Debug, Default)]
pub struct PunchClock {
clock: Clock,
log: HashMap<String, SystemTime>,
}
impl PunchClock {
pub fn new<C: Into<Clock>>(clock: C) -> Self {
Self { clock: clock.into(), ..Default::default() }
}
pub fn ingress<S: Into<String>>(&mut self, name: S) -> Result<SystemTime, PunchError> {
let name = name.into();
if self.log.contains_key(&name) {
return Err(PunchError::AlreadyPunchedIn(name));
}
let now = self.clock.now();
self.log.insert(name, now);
Ok(now)
}
pub fn egress<S: Into<String>>(&mut self, name: S) -> Result<Duration, PunchError> {
let name = name.into();
match self.log.remove(&name) {
Some(t) => {
self.clock.now().duration_since(t).map_err(|_| PunchError::InvalidPunchOutTime)
}
None => Err(PunchError::NotPunchedIn(name)),
}
}
}
fn main() {
let clock = FakeClock::default();
let mut punch_clock = PunchClock::new(clock.clone());
let t = punch_clock.ingress("Jimmy").unwrap();
println!("Jimmy punched in at: {:?}", t);
let ttl = Duration::from_secs(42);
clock.advance(ttl);
let since = punch_clock.egress("Jimmy").unwrap();
println!("Jimmy punched out after: {:?}", since);
assert_eq!(ttl, since)
}