From 236cf191b90a428325c8c179d595d4b1cd36f776 Mon Sep 17 00:00:00 2001 From: Mx Kookie Date: Sun, 13 Dec 2020 12:08:55 +0000 Subject: cassiopeia: changing parser output to more generic IR structure This allows a few things: a, it's a persistant format that we can mirror to disk again, AND can adapt because all type information is known, and it allows for new entries to be added to the IR more easily, without having to worry about exact formatting, or re-inferring order from the TimeFile abstraction. --- apps/cassiopeia/src/data.rs | 159 +++++++++++++++++++++++++++-------- apps/cassiopeia/src/date.rs | 26 ++++++ apps/cassiopeia/src/format/ir.rs | 63 ++++++++++++++ apps/cassiopeia/src/format/mod.rs | 32 +++++-- apps/cassiopeia/src/format/parser.rs | 3 + apps/cassiopeia/src/lib.rs | 30 ++++--- apps/cassiopeia/src/time.rs | 29 ++++++- 7 files changed, 289 insertions(+), 53 deletions(-) create mode 100644 apps/cassiopeia/src/date.rs create mode 100644 apps/cassiopeia/src/format/ir.rs (limited to 'apps/cassiopeia') diff --git a/apps/cassiopeia/src/data.rs b/apps/cassiopeia/src/data.rs index 7442d699be88..47bb6e35b6e5 100644 --- a/apps/cassiopeia/src/data.rs +++ b/apps/cassiopeia/src/data.rs @@ -4,14 +4,15 @@ //! used to generate new files, and perform various lookups and //! analysis tasks. -use crate::format::LineCfg; -use chrono::{DateTime, Duration, Local, FixedOffset as Offset, NaiveDate}; +use crate::{ + format::{IrItem, IrType, MakeIr}, + Date, Time, +}; +use chrono::{DateTime, Duration, FixedOffset as Offset, Local, NaiveDate}; use std::collections::BTreeMap; #[derive(Debug, Default)] pub struct TimeFile { - /// Raw line buffers to echo back into the file - lines: Vec, /// A parsed header structure header: BTreeMap, /// A parsed session structure @@ -21,70 +22,158 @@ pub struct TimeFile { } impl TimeFile { - pub(crate) fn append(mut self, line: LineCfg) -> Self { - let lo = self.lines.len(); + pub(crate) fn append(&mut self, line: IrItem) { match line { - LineCfg::Header(ref header) => self.header = header.clone(), - LineCfg::Start(Some(time)) => self.sessions.push(Session::start(time, lo)), - LineCfg::Stop(Some(time)) => self.get_last_session().stop(time, lo), - LineCfg::Invoice(Some(date)) => self.invoices.push(Invoice::new(date, lo)), + 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())), _ => {} } + } - self.lines.push(line); - self + fn get_last_session(&mut self) -> Option<&mut Session> { + self.sessions.last_mut() } - fn get_last_session(&mut self) -> &mut Session { - self.sessions.last_mut().unwrap() + fn get_last_invoice(&mut self) -> Option<&mut Invoice> { + self.invoices.last_mut() } /// Start a new session (optionally 15-minute rounded) - pub fn start(&mut self, round: bool) -> Option<()> { - let now = Local::now(); - + /// + /// 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 + _ => {} + } + + self.invoices.push(Invoice::new(today)); Some(()) } } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Session { - start: DateTime, - stop: Option>, - /// Track the lines this session took place in - lines: (usize, usize), + start: Time, + stop: Option