diff options
author | Kaiden Fey <kookie@spacekookie.de> | 2021-02-21 14:56:11 +0100 |
---|---|---|
committer | Kaiden Fey <kookie@spacekookie.de> | 2021-02-21 14:56:11 +0100 |
commit | f186a7345dfc99347673f46e0daff0cb63ac8492 (patch) | |
tree | 5179ffd4654b80ea11a1656e28ef244439648ba4 /apps/koffice/libko/src/cass/mod.rs | |
parent | effbdeed66e8de8e769b8ac069926ad1a9110e62 (diff) |
k-office: initial code dumpk-office/init
Diffstat (limited to 'apps/koffice/libko/src/cass/mod.rs')
-rw-r--r-- | apps/koffice/libko/src/cass/mod.rs | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/apps/koffice/libko/src/cass/mod.rs b/apps/koffice/libko/src/cass/mod.rs new file mode 100644 index 000000000000..43dd79b0fbd7 --- /dev/null +++ b/apps/koffice/libko/src/cass/mod.rs @@ -0,0 +1,160 @@ +//! Cassiopeia plain text time tracking tool +//! +//! Versions `0.1` and `0.2` were written in Ruby and are thus +//! deprecated. Most likely you are interested in `cass(1)`, the +//! simple plain text time tracking utility, part of the kookie-office +//! suite of commandline tools! This is the library powering it. +//! +//! For more documentation, check out: +//! https://git.spacekookie.de/kookienomicon/tree/apps/cassiopeia + +mod data; +mod date; +mod format; +mod time; +mod timeline; + +pub mod error; +pub mod meta; + +pub use date::Date; +pub use time::Time; + +pub(crate) use data::TimeFile; +use data::{Invoice, Session}; +use error::{ParseError, ParseResult}; +use format::{ + ir::{append_ir, clean_ir, IrStream, MakeIr}, + ParseOutput, +}; + +/// A state handler and primary API for all cass interactions +/// +/// +pub struct Cassiopeia { + path: String, + tf: TimeFile, + ir: IrStream, +} + +impl Cassiopeia { + /// Load a cass file from disk, parsing it into a [`TimeFile`](crate::TimeFile) + pub fn load(path: &str) -> ParseResult<Self> { + let path = path.to_owned(); + format::load_file(path.as_str()).map(|ParseOutput { tf, ir }| Self { path, tf, ir }) + } + + pub(crate) fn timefile(&self) -> TimeFile { + self.tf.clone() + } + + /// Start a new work session (with optional 15 minute rounding) + pub fn start(&mut self, round: bool) -> ParseResult<()> { + let delta = self.tf.timeline.start(Time::rounded(round))?; + clean_ir(&mut self.ir); + append_ir(&mut self.ir, delta.make_ir()); + format::write_file(self.path.as_str(), &mut self.ir) + } + + /// Stop the existing work session (with optional 15 minute rounding) + pub fn stop(&mut self, round: bool) -> ParseResult<()> { + let delta = self.tf.timeline.stop(Time::rounded(round))?; + clean_ir(&mut self.ir); + append_ir(&mut self.ir, delta.make_ir()); + format::write_file(self.path.as_str(), &mut self.ir) + } + + /// Add an invoice block to the time file + pub fn invoice<'slf>(&'slf mut self) -> Invoicer<'slf> { + Invoicer::new(self) + } + + /// Write out the file IR as is, updating only the header version + pub fn update(&mut self) -> ParseResult<()> { + clean_ir(&mut self.ir); + format::write_file(self.path.as_str(), &mut self.ir) + } + + /// Collect statistics on previous work sessions + pub fn stat(&self) -> ParseResult<String> { + todo!() + } +} + +/// An invoice generator builder +/// +/// The most simple use-case of this type is to provide no parameters +/// and simply add an `INVOICE` line to the cass file. Adittionally +/// you may provide the client and project name, which will then +/// require the `client_db` path to be set as well. +/// +/// ```rust,no_run +/// # let mut cass = cassiopeia::Cassiopeia::load("").unwrap(); +/// cass.invoice().run(); +/// ``` +/// +/// Additional errors can be thrown if the client or project are not +/// known in the client db. +/// +/// ```rust,no_run +/// # let mut cass = cassiopeia::Cassiopeia::load("").unwrap(); +/// cass.invoice() +/// .generate() +/// .db("/home/office/clients.yml".into()) +/// .client("ACME".into()) +/// .run(); +/// ``` +#[allow(unused)] +pub struct Invoicer<'cass> { + tf: &'cass mut Cassiopeia, + generate: bool, + client_db: String, + client: String, + project: String, +} + +impl<'cass> Invoicer<'cass> { + pub fn new(tf: &'cass mut Cassiopeia) -> Self { + Self { + tf, + generate: false, + client_db: String::new(), + client: String::new(), + project: String::new(), + } + } + + /// Enable the invoice generation feature + pub fn generate(self) -> Self { + Self { + generate: true, + ..self + } + } + + /// Provide the client database file (.yml format) + pub fn db(self, client_db: String) -> Self { + Self { client_db, ..self } + } + + /// Provide the client to invoice + pub fn client(self, client: String) -> Self { + Self { client, ..self } + } + + pub fn project(self, project: String) -> Self { + Self { project, ..self } + } + + pub fn run(mut self) -> ParseResult<()> { + if self.generate { + eprintln!("Integration with invoice(1) is currently not implemented. Sorry :("); + return Err(ParseError::Unknown); + } + + let delta = self.tf.tf.timeline.invoice(Date::today())?; + clean_ir(&mut self.tf.ir); + append_ir(&mut self.tf.ir, delta.make_ir()); + format::write_file(self.tf.path.as_str(), &mut self.tf.ir) + } +} |