aboutsummaryrefslogtreecommitdiff
path: root/apps/cassiopeia/src/data.rs
diff options
context:
space:
mode:
authorMx Kookie <kookie@spacekookie.de>2020-12-13 12:08:55 +0000
committerMx Kookie <kookie@spacekookie.de>2020-12-21 05:19:49 +0100
commit236cf191b90a428325c8c179d595d4b1cd36f776 (patch)
tree5d6c67cb0b7ef980aad47ee35e264b4dfdb5422d /apps/cassiopeia/src/data.rs
parent4c97f3208a0ba185264a169e01d0b0d922266ea6 (diff)
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.
Diffstat (limited to 'apps/cassiopeia/src/data.rs')
-rw-r--r--apps/cassiopeia/src/data.rs159
1 files changed, 124 insertions, 35 deletions
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<LineCfg>,
/// A parsed header structure
header: BTreeMap<String, String>,
/// 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<Session> {
+ // 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<Session> {
+ 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<Offset>,
- stop: Option<DateTime<Offset>>,
- /// Track the lines this session took place in
- lines: (usize, usize),
+ start: Time,
+ stop: Option<Time>,
}
impl Session {
/// Create a new session with a start time
- fn start(start: DateTime<Offset>, line: usize) -> Self {
- Self {
- start,
- stop: None,
- lines: (line, 0),
- }
+ fn start(start: Time) -> Self {
+ Self { start, stop: None }
}
/// Finalise a session with a stop time
- fn stop(&mut self, stop: DateTime<Offset>, line: usize) {
+ fn stop(&mut self, stop: Time) {
self.stop = Some(stop);
- self.lines.1 = line;
+ }
+
+ /// 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.map(|stop| stop - self.start)
+ self.stop.as_ref().map(|stop| stop - &self.start)
+ }
+}
+
+impl MakeIr for Session {
+ fn make_ir(&self) -> IrType {
+ match self.stop {
+ Some(ref time) => IrType::Stop(time.clone()),
+ None => IrType::Start(self.start.clone()),
+ }
}
}
#[derive(Debug)]
pub struct Invoice {
- date: NaiveDate,
- line: usize,
+ date: Date,
}
impl Invoice {
- fn new(date: NaiveDate, line: usize) -> Self {
- Self { date, line }
+ fn new(date: Date) -> Self {
+ Self { date }
+ }
+}
+
+impl MakeIr for Invoice {
+ fn make_ir(&self) -> IrType {
+ IrType::Invoice(self.date.clone())
}
}