//! 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::{ format::ir::{IrItem, IrType, MakeIr}, 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 header: BTreeMap, /// A parsed session structure sessions: Vec, /// A parsed invoice list invoices: Vec, } impl TimeFile { pub(crate) fn append(&mut self, line: IrItem) { match line { IrItem { tt: IrType::Header(ref header), .. } => self.header = header.clone(), IrItem { tt: IrType::Start(time), lo, } => self.sessions.push(Session::start(time.into())), IrItem { tt: IrType::Stop(time), lo, } => self.get_last_session().unwrap().stop(time.into()), IrItem { tt: IrType::Invoice(date), lo, } => self.invoices.push(Invoice::new(date.into())), _ => {} } } fn get_last_session(&mut self) -> Option<&mut Session> { self.sessions.last_mut() } fn get_last_invoice(&mut self) -> Option<&mut Invoice> { self.invoices.last_mut() } /// Start a new session (optionally 15-minute rounded) /// /// This function returns the new session object that will have to /// be turned into an IR line to be written back into the file pub(crate) fn start(&mut self, round: bool) -> Option { // Check if the last session was closed match self.get_last_session() { Some(s) if !s.finished() => return None, _ => {} } // Create a new time let now = if round { Time::now().round() } else { Time::now() }; Some(Session::start(now)) } /// Stop the last session that was started, returning a completed /// session pub(crate) fn stop(&mut self, round: bool) -> Option { match self.get_last_session() { Some(s) if s.finished() => return None, None => return None, _ => {} } // Create a new time let now = if round { Time::now().round() } else { Time::now() }; self.get_last_session().cloned().map(|mut s| { s.stop(now); s }) } /// Add a new invoice block to the time file pub(crate) fn invoice(&mut self) -> Option { let today = Date::today(); let last_sess = self.get_last_session().cloned(); match self.get_last_invoice() { // Check if _today_ there has been already an invoice Some(i) if i.date == today => return None, // Check if since the last invoice there has been at least // _one_ terminated session. Some(i) if !last_sess .map(|s| !s.stop.map(|s| s.after(&i.date)).unwrap_or(false)) .unwrap_or(false) => { return None } // Otherwise, we create an invoice _ => {} } Some(Invoice::new(today)) } } #[derive(Clone, Debug)] pub struct Session { start: Time, stop: Option