use crate::Io; use chrono::{NaiveDate, Utc}; use serde::{Deserialize, Serialize}; use std::{fs::File, io::Read, path::Path, string::ToString}; /// A specification to build invoice IDs with #[derive(Serialize, Deserialize)] pub enum InvoiceId { YearMonthId(u16, u8, usize), } impl InvoiceId { pub(crate) fn date() -> Self { let now = Utc::now().naive_local().date(); let year = now.format("%Y").to_string(); let month = now.format("%m").to_string(); Self::YearMonthId( str::parse(&year).unwrap_or_else(|_| { fatal!( "Current year doesn't fit into u16. WHEN ARE YOU USING THIS CODE???????? (have you abolished capitalism yet? uwu)" ) }), str::parse(&month) .unwrap_or_else(|_| fatal!("Invalid month value (doesn't fit in u8)")), 0, ) } /// Utility to find the next invoice ID in a sequence /// /// Start with an InvoiceID that only has the correct date /// (`date()`), then call this function for each existing invoice /// in your collection. If the current ID is lower or equal than /// the one given, take the given ID and increment it by one. /// /// Once this has been done for all invoices in the collection, /// you are guaranteed to have the latest invoice ID. pub(crate) fn find_next(mut self, i: &Invoice) -> Self { if self.numeric() <= i.id.numeric() { self.update_numeric(i.id.numeric() + 1); } self } fn update_numeric(&mut self, id: usize) { match self { Self::YearMonthId(_, _, ref mut _id) => { *_id = id; } } } /// A valid invoice ID needs to have a numeric element pub(crate) fn numeric(&self) -> usize { match self { Self::YearMonthId(_, _, id) => *id, } } } impl ToString for InvoiceId { fn to_string(&self) -> String { match self { Self::YearMonthId(yr, mo, id) => format!("#{}-{:02}-{:04}", yr, mo, id), } } } /// An invoice for a specific project #[derive(Serialize, Deserialize)] pub struct Invoice { id: InvoiceId, client: String, project: String, date: NaiveDate, amount: usize, currency: String, vat: u8, } impl Invoice { pub fn load(path: &Path) -> Option { let mut buf = String::new(); let mut f = File::open(path).ok()?; f.read_to_string(&mut buf).ok()?; Some(Self::from_yaml(buf)) } } #[test] fn invoice_id_fmt() { let inv_id = InvoiceId::YearMonthId(2020, 06, 0055); assert_eq!(inv_id.to_string(), "#2020-06-0055".to_string()); }