aboutsummaryrefslogtreecommitdiff
path: root/apps/cassiopeia/src/lib.rs
blob: d4ebf5355bb9161a0e5ead8fef7bd3d7eaaccf7f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//! 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(())
    }
}