parent
d346c9f2f3
commit
4c97f3208a
@ -1,8 +1,129 @@ |
|||||||
//! Cassiopeia plain text time tracking tool
|
//! 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 data; |
||||||
mod format; |
mod format; |
||||||
pub mod meta; |
pub mod meta; |
||||||
|
mod time; |
||||||
|
|
||||||
pub use data::{Session, TimeFile}; |
pub use data::{Session, TimeFile}; |
||||||
pub use format::load_file; |
pub use format::load_file; |
||||||
|
|
||||||
|
/// A state handler for all cass interactions
|
||||||
|
///
|
||||||
|
/// This could be a stateless API, but I like being able to refer to
|
||||||
|
/// fields that need to be saved for later here. This API wraps
|
||||||
|
/// around [`TimeFile`](crate::TimeFile), so that you don't have to! ✨
|
||||||
|
pub struct Cassiopeia { |
||||||
|
path: String, |
||||||
|
tf: TimeFile, |
||||||
|
} |
||||||
|
|
||||||
|
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(|tf| Self { path, tf }) |
||||||
|
} |
||||||
|
|
||||||
|
/// 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) |
||||||
|
} |
||||||
|
|
||||||
|
/// Stop the existing work session (with optional 15 minute rounding)
|
||||||
|
pub fn stop(&mut self, round: bool) -> Option<()> { |
||||||
|
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(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// S
|
||||||
|
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; |
||||||
|
} |
||||||
|
|
||||||
|
None |
||||||
|
} |
||||||
|
} |
||||||
|
Loading…
Reference in new issue