You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
178 lines
4.6 KiB
178 lines
4.6 KiB
//! 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<String, String>,
|
|
/// A parsed session structure
|
|
sessions: Vec<Session>,
|
|
/// A parsed invoice list
|
|
invoices: Vec<Invoice>,
|
|
}
|
|
|
|
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<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<Invoice> {
|
|
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<Time>,
|
|
}
|
|
|
|
impl Session {
|
|
/// Create a new session with a start time
|
|
fn start(start: Time) -> Self {
|
|
Self { start, stop: None }
|
|
}
|
|
|
|
/// Finalise a session with a stop time
|
|
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)
|
|
}
|
|
}
|
|
|
|
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: Date,
|
|
}
|
|
|
|
impl Invoice {
|
|
fn new(date: Date) -> Self {
|
|
Self { date }
|
|
}
|
|
}
|
|
|
|
impl MakeIr for Invoice {
|
|
fn make_ir(&self) -> IrType {
|
|
IrType::Invoice(self.date.clone())
|
|
}
|
|
}
|
|
|