//! Typed time file for cassiopeia //! //! This data gets generated by the `format` module, and can later be //! used to generate new files, and perform various lookups and //! analysis tasks. use crate::{ error::{ParseError, ParseResult, UserResult}, format::ir::{IrItem, IrType, MakeIr}, timeline::{Entry, Timeline}, Date, Time, }; use chrono::{DateTime, Duration, FixedOffset as Offset, Local, NaiveDate}; use std::collections::BTreeMap; #[derive(Debug, Default)] pub struct TimeFile { /// A parsed header structure pub(crate) header: BTreeMap<String, String>, /// A parsed timeline of events pub(crate) timeline: Timeline, } impl TimeFile { /// Append entries to the timeline from the parsed IR /// /// Report any errors that occur back to the parser, that will /// print a message to the user and terminate the program. pub(crate) fn append(&mut self, line: IrItem) -> ParseResult<()> { match line { IrItem { tt: IrType::Header(ref header), .. } => Ok(header.iter().for_each(|(k, v)| { self.header.insert(k.clone(), v.clone()); })), IrItem { tt: IrType::Start(time), lo, } => Ok(self.timeline.start(time).map(|_| ())?), IrItem { tt: IrType::Stop(time), lo, } => Ok(self.timeline.stop(time).map(|_| ())?), IrItem { tt: IrType::Invoice(date), lo, } => Ok(self.timeline.invoice(date).map(|_| ())?), _ => Err(ParseError::Unknown), } } } #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Session { start: Time, stop: Option<Time>, } impl Session { /// Create a new session with a start time pub(crate) fn start(start: Time) -> Self { Self { start, stop: None } } /// Finalise a session with a stop time pub(crate) fn stop(&mut self, stop: Time) { self.stop = Some(stop); } /// Check whether this session was already finished pub fn finished(&self) -> bool { self.stop.is_some() } /// Get the length of the session, if it was already finished pub fn length(&self) -> Option<Duration> { self.stop.as_ref().map(|stop| stop - &self.start) } } #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Invoice { pub(crate) date: Date, } impl Invoice { pub(crate) fn new(date: Date) -> Self { Self { date } } } /// Changes to the timeline are encoded in a delta pub(crate) enum Delta { Start(Time), Stop(Time), Invoice(Date), } impl MakeIr for Delta { fn make_ir(&self) -> IrType { match self { Self::Start(ref time) => IrType::Start(time.clone()), Self::Stop(ref time) => IrType::Stop(time.clone()), Self::Invoice(ref date) => IrType::Invoice(date.clone()), } } }