koffice: improve cass error handling, reporting, and logging

wip/yesman
Katharina Fey 3 years ago
parent 34e3e84eb4
commit 4bbd0b2137
Signed by: kookie
GPG Key ID: 90734A9E619C8A6C
  1. 4
      apps/koffice/libko/src/cass/data.rs
  2. 44
      apps/koffice/libko/src/cass/error.rs
  3. 20
      apps/koffice/libko/src/cass/format/ir.rs
  4. 14
      apps/koffice/libko/src/cass/timeline.rs

@ -54,7 +54,9 @@ impl TimeFile {
tt: IrType::Invoice(date),
lo,
} => Ok(self.timeline.invoice(date).map(|_| ())?),
_ => Err(ParseError::Unknown),
_ => Ok(()), // Any other IrType will be ignored, and thus
// we simply return Ok(()) to signal to the
// parser to keep going.
}
}
}

@ -1,5 +1,5 @@
//! A set of error types for cassiopeia
use crate::cass::{Date, Time};
use std::fmt::{self, Display, Formatter};
use std::{error::Error, io};
@ -12,18 +12,28 @@ use std::{error::Error, io};
#[derive(Debug)]
pub enum UserError {
/// Trying to start a session when one exists
ActiveSessionExists,
ActiveSessionExists(Time),
/// Trying to stop a session when none exists
NoActiveSession,
NoActiveSession(Time),
/// Trying to create a second invoice on the same day
SameDayInvoice,
SameDayInvoice(Date),
/// No work was done since the last invoice
NoWorkInvoice,
NoWorkInvoice(Date),
}
impl Display for UserError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "You're doing it wrong!")
write!(
f,
"{}",
match self {
Self::ActiveSessionExists(t) =>
format!("Session since '{}' in progress", t.to_string()),
Self::NoActiveSession(t) => format!("No session before '{}'", t.to_string()),
Self::SameDayInvoice(d) => format!("More than one invoice on '{}'", d.to_string()),
Self::NoWorkInvoice(d) => format!("No work done before '{}'", d.to_string()),
}
)
}
}
@ -59,13 +69,33 @@ pub enum ParseError {
BadTimestamp { line: usize, tokn: String },
/// A bad date was found
BadDate { line: usize, tokn: String },
/// An unexpected sequence was found
Unexpected { line: usize, tokn: String },
/// An unknown parse error occured
Unknown,
}
impl Display for ParseError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "The parsed file was bad :(")
write!(
f,
"{}",
match self {
Self::User(err) => format!("User error: {}", err),
Self::NoSuchFile => "The file does not exist".into(),
Self::BadPermissions => "Insufficient permissions to read the file".into(),
Self::FileNotWritable => "Insufficient permissions to write the file".into(),
Self::FileUnknown(file) => format!("System I/O error: {}", file),
Self::BadKeyword { line, tokn } =>
format!("Invalid keyword '{}' on line {}", tokn, line),
Self::BadTimestamp { line, tokn } =>
format!("Invalid timestamp '{}' on line {}", tokn, line),
Self::BadDate { line, tokn } => format!("Invalid date '{}' on line {}", tokn, line),
Self::Unexpected { line, tokn } =>
format!("Unexpected token '{}' on line {}", tokn, line),
Self::Unknown => "An unknown error occured".into(),
}
)
}
}

@ -1,5 +1,5 @@
use crate::cass::{format::LineCfg, Date, Time, TimeFile};
use std::collections::BTreeMap;
use std::{collections::BTreeMap, string::ToString};
/// A set of IR parsed items that makes up a whole cass file
pub(crate) type IrStream = Vec<IrItem>;
@ -39,6 +39,24 @@ pub(crate) enum IrType {
Ignore,
}
impl ToString for IrType {
fn to_string(&self) -> String {
match self {
Self::Header(map) => format!(
"Header map [{}]",
map.iter().fold(String::new(), |mut s, (k, v)| {
s.push_str(&format!("{}={}", k, v));
s
})
),
Self::Start(t) => format!("Start (time = {})", t.to_string()),
Self::Stop(t) => format!("Stop (time = {})", t.to_string()),
Self::Invoice(d) => format!("Invoice (date = {})", d.to_string()),
Self::Ignore => "Ignored line".into(),
}
}
}
/// Generate a stream of IR items from the raw parser output
pub(crate) fn generate_ir(buf: impl Iterator<Item = LineCfg>) -> IrStream {
buf.enumerate().fold(vec![], |mut buf, (lo, item)| {

@ -92,19 +92,22 @@ impl Timeline {
/// Start a new session, if no active session is already in progress
pub fn start(&mut self, time: Time) -> UserResult<Delta> {
trace!("Starting session {}", time.to_string());
match self.last_session() {
Some(s) if !s.finished() => Err(UserError::ActiveSessionExists),
Some(s) if !s.finished() => Err(UserError::ActiveSessionExists(time.clone())),
_ => Ok(()),
}?;
self.inner.push(Session::start(time.clone()).into());
self.last_session();
Ok(Delta::Start(time))
}
/// Stop an ongoing session, if one exists
pub fn stop(&mut self, time: Time) -> UserResult<Delta> {
trace!("Stopping session {}", time.to_string());
match self.last_session() {
Some(s) if s.finished() => Err(UserError::NoActiveSession),
Some(s) if s.finished() => Err(UserError::NoActiveSession(time.clone())),
_ => Ok(()),
}?;
@ -114,11 +117,14 @@ impl Timeline {
/// Create a new invoice on the given day
pub fn invoice(&mut self, date: Date) -> UserResult<Delta> {
trace!("Applying invoice {}", date.to_string());
match self.last_invoice() {
// If an invoice on the same day exists already
Some(i) if i.date == date => Err(UserError::SameDayInvoice),
Some(i) if i.date == date => Err(UserError::SameDayInvoice(date.clone())),
// If there was no work since the last invoice
Some(ref i) if self.session_iter(&i.date).len() == 0 => Err(UserError::NoWorkInvoice),
Some(ref i) if self.session_iter(&i.date).len() == 0 => {
Err(UserError::NoWorkInvoice(date.clone()))
}
// Otherwise everything is coolio
_ => Ok(()),
}?;

Loading…
Cancel
Save