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.
139 lines
3.5 KiB
139 lines
3.5 KiB
//! 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;
|
|
pub mod meta;
|
|
mod time;
|
|
|
|
pub use date::Date;
|
|
pub use format::load_file;
|
|
pub use time::Time;
|
|
|
|
use data::{Invoice, Session, TimeFile};
|
|
use format::{ir, IrStream, 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) -> Option<Self> {
|
|
let path = path.to_owned();
|
|
load_file(path.as_str()).map(|ParseOutput { tf, ir }| Self { path, tf, ir })
|
|
}
|
|
|
|
/// Store the modified time file back to disk
|
|
pub fn store(&self) -> Option<()> {
|
|
Some(())
|
|
}
|
|
|
|
/// Start a new work session (with optional 15 minute rounding)
|
|
pub fn start(&mut self, round: bool) -> Option<()> {
|
|
self.tf.start(round)?;
|
|
|
|
Some(())
|
|
}
|
|
|
|
/// Stop the existing work session (with optional 15 minute rounding)
|
|
pub fn stop(&mut self, round: bool) -> Option<()> {
|
|
self.tf.stop(round)?;
|
|
|
|
Some(())
|
|
}
|
|
|
|
/// Add an invoice block to the time file
|
|
pub fn invoice<'slf>(&'slf mut self) -> Invoicer<'slf> {
|
|
Invoicer::new(self)
|
|
}
|
|
}
|
|
|
|
/// 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();
|
|
/// ```
|
|
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(self) -> Option<()> {
|
|
if self.generate {
|
|
eprintln!("Integration with invoice(1) is currently not implemented. Sorry :(");
|
|
return None;
|
|
}
|
|
|
|
self.tf.tf.invoice()?;
|
|
|
|
Some(())
|
|
}
|
|
}
|
|
|