aboutsummaryrefslogtreecommitdiff
path: root/apps/koffice/libko/src/cass/timeline.rs
diff options
context:
space:
mode:
Diffstat (limited to 'apps/koffice/libko/src/cass/timeline.rs')
-rw-r--r--apps/koffice/libko/src/cass/timeline.rs132
1 files changed, 132 insertions, 0 deletions
diff --git a/apps/koffice/libko/src/cass/timeline.rs b/apps/koffice/libko/src/cass/timeline.rs
new file mode 100644
index 000000000000..057f0b47d7da
--- /dev/null
+++ b/apps/koffice/libko/src/cass/timeline.rs
@@ -0,0 +1,132 @@
+use crate::cass::{
+ data::{Delta, Invoice, Session},
+ error::{UserError, UserResult},
+ Date, Time,
+};
+
+/// A timeline entry of sessions and invoices
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub enum Entry {
+ Session(Session),
+ Invoice(Invoice),
+}
+
+impl From<Session> for Entry {
+ fn from(s: Session) -> Self {
+ Self::Session(s)
+ }
+}
+
+impl From<Invoice> for Entry {
+ fn from(i: Invoice) -> Self {
+ Self::Invoice(i)
+ }
+}
+
+/// A timeline of sessions and invoices, ordered chronologically
+#[derive(Debug, Default, Clone)]
+pub struct Timeline {
+ inner: Vec<Entry>,
+}
+
+impl Timeline {
+ /// Take a set of sessions and invoices to sort into a timeline
+ pub fn build(s: Vec<Session>, i: Vec<Invoice>) -> Self {
+ let mut inner: Vec<_> = s.into_iter().map(|s| Entry::Session(s)).collect();
+ inner.append(&mut i.into_iter().map(|i| Entry::Invoice(i)).collect());
+ Self { inner }
+ }
+
+ /// Utility function to get the last session in the timeline
+ fn last_session(&mut self) -> Option<&mut Session> {
+ self.inner
+ .iter_mut()
+ .find(|e| match e {
+ Entry::Session(_) => true,
+ _ => false,
+ })
+ .map(|e| match e {
+ Entry::Session(ref mut s) => s,
+ _ => unreachable!(),
+ })
+ }
+
+ /// Utility function to get the last invoice in the timeline
+ fn last_invoice(&self) -> Option<&Invoice> {
+ self.inner
+ .iter()
+ .find(|e| match e {
+ Entry::Invoice(_) => true,
+ _ => false,
+ })
+ .map(|e| match e {
+ Entry::Invoice(ref s) => s,
+ _ => unreachable!(),
+ })
+ }
+
+ /// Get a list of sessions that happened up to a certain invoice date
+ ///
+ /// **WARNING** If there is no invoice with the given date, this
+ /// function will return garbage data, so don't call it with
+ /// invoice dates that don't exist.
+ ///
+ /// Because: if the date passes other invoices on the way, the accumulator
+ /// will be discarded and a new count will be started.
+ pub fn session_iter(&self, date: &Date) -> Vec<&Session> {
+ self.inner
+ .iter()
+ .fold((false, vec![]), |(mut done, mut acc), entry| {
+ match (done, entry) {
+ // Put sessions into the accumulator
+ (false, Entry::Session(ref s)) => acc.push(s),
+ // When we reach the target invoice, terminate the iterator
+ (false, Entry::Invoice(ref i)) if &i.date == date => done = true,
+ // When we hit another invoice, empty accumulator
+ (false, Entry::Invoice(_)) => acc.clear(),
+ // When we are ever "done", skip all other entries
+ (true, _) => {}
+ }
+
+ (done, acc)
+ })
+ .1
+ }
+
+ /// Start a new session, if no active session is already in progress
+ pub fn start(&mut self, time: Time) -> UserResult<Delta> {
+ match self.last_session() {
+ Some(s) if !s.finished() => Err(UserError::ActiveSessionExists),
+ _ => Ok(()),
+ }?;
+
+ self.inner.push(Session::start(time.clone()).into());
+ Ok(Delta::Start(time))
+ }
+
+ /// Stop an ongoing session, if one exists
+ pub fn stop(&mut self, time: Time) -> UserResult<Delta> {
+ match self.last_session() {
+ Some(s) if s.finished() => Err(UserError::NoActiveSession),
+ _ => Ok(()),
+ }?;
+
+ self.last_session().unwrap().stop(time.clone());
+ Ok(Delta::Stop(time))
+ }
+
+ /// Create a new invoice on the given day
+ pub fn invoice(&mut self, date: Date) -> UserResult<Delta> {
+ match self.last_invoice() {
+ // If an invoice on the same day exists already
+ Some(i) if i.date == date => Err(UserError::SameDayInvoice),
+ // If there was no work since the last invoice
+ Some(ref i) if self.session_iter(&i.date).len() == 0 => Err(UserError::NoWorkInvoice),
+ // Otherwise everything is coolio
+ _ => Ok(()),
+ }?;
+
+ self.inner.push(Invoice::new(date.clone()).into());
+ Ok(Delta::Invoice(date))
+ }
+}