diff --git a/cli/src/commands/check.rs b/cli/src/commands/check.rs index 4b2866e..c8a0c1d 100644 --- a/cli/src/commands/check.rs +++ b/cli/src/commands/check.rs @@ -1,10 +1,9 @@ use super::OutputFormat; -use crate::error::AppError; -use acari_lib::{Account, Client, User}; +use acari_lib::{AcariError, Account, Client, User}; use prettytable::{cell, format, row, table}; use serde_json::json; -pub fn check(client: &dyn Client, output_format: OutputFormat) -> Result<(), AppError> { +pub fn check(client: &dyn Client, output_format: OutputFormat) -> Result<(), AcariError> { let account = client.get_account()?; let user = client.get_myself()?; @@ -47,7 +46,7 @@ fn print_pretty(account: Account, user: User) { user_table.printstd(); } -fn print_json(account: Account, user: User) -> Result<(), AppError> { +fn print_json(account: Account, user: User) -> Result<(), AcariError> { println!( "{}", serde_json::to_string_pretty(&json!({ diff --git a/cli/src/commands/customers.rs b/cli/src/commands/customers.rs index 9c3497f..000c206 100644 --- a/cli/src/commands/customers.rs +++ b/cli/src/commands/customers.rs @@ -1,9 +1,8 @@ use super::OutputFormat; -use crate::error::AppError; -use acari_lib::{Client, Customer}; +use acari_lib::{AcariError, Client, Customer}; use prettytable::{cell, format, row, Table}; -pub fn customers(client: &dyn Client, output_format: OutputFormat) -> Result<(), AppError> { +pub fn customers(client: &dyn Client, output_format: OutputFormat) -> Result<(), AcariError> { let mut customers = client.get_customers()?; customers.sort_by(|c1, c2| c1.name.cmp(&c2.name)); @@ -28,7 +27,7 @@ fn print_pretty(customers: Vec) { customers_table.printstd(); } -fn print_json(customers: Vec) -> Result<(), AppError> { +fn print_json(customers: Vec) -> Result<(), AcariError> { println!("{}", serde_json::to_string_pretty(&customers)?); Ok(()) diff --git a/cli/src/commands/entries.rs b/cli/src/commands/entries.rs index ddde42e..bee7a72 100644 --- a/cli/src/commands/entries.rs +++ b/cli/src/commands/entries.rs @@ -1,11 +1,10 @@ use super::OutputFormat; -use crate::error::AppError; -use acari_lib::{Client, DateSpan, TimeEntry}; +use acari_lib::{AcariError, Client, DateSpan, TimeEntry}; use chrono::NaiveDate; use itertools::Itertools; use prettytable::{cell, row, table}; -pub fn entries(client: &dyn Client, output_format: OutputFormat, date_span: DateSpan) -> Result<(), AppError> { +pub fn entries(client: &dyn Client, output_format: OutputFormat, date_span: DateSpan) -> Result<(), AcariError> { let mut time_entries = client.get_time_entries(date_span)?; time_entries.sort_by(|t1, t2| t1.date_at.cmp(&t2.date_at)); @@ -33,7 +32,7 @@ fn print_pretty(entries: Vec<(&NaiveDate, Vec<&TimeEntry>)>) { entries_table.printstd(); } -fn print_json(entries: Vec) -> Result<(), AppError> { +fn print_json(entries: Vec) -> Result<(), AcariError> { println!("{}", serde_json::to_string_pretty(&entries)?); Ok(()) diff --git a/cli/src/commands/init.rs b/cli/src/commands/init.rs index 16ffaf9..0ecd497 100644 --- a/cli/src/commands/init.rs +++ b/cli/src/commands/init.rs @@ -1,15 +1,15 @@ use crate::config::Config; -use crate::error::AppError; +use acari_lib::AcariError; use std::io::{stdout, Write}; use text_io::try_read; -pub fn init() -> Result<(), AppError> { +pub fn init() -> Result<(), AcariError> { print!("Mite domain: "); stdout().flush()?; - let domain: String = try_read!("{}\n")?; + let domain: String = try_read!("{}\n").map_err(|e| AcariError::InternalError(format!("IO: {}", e)))?; print!("API Token: "); stdout().flush()?; - let token: String = try_read!("{}\n")?; + let token: String = try_read!("{}\n").map_err(|e| AcariError::InternalError(format!("IO: {}", e)))?; Config { domain, diff --git a/cli/src/commands/mod.rs b/cli/src/commands/mod.rs index 2ecc3d8..868510e 100644 --- a/cli/src/commands/mod.rs +++ b/cli/src/commands/mod.rs @@ -12,7 +12,7 @@ pub use init::*; pub use projects::*; pub use services::*; -use crate::error::AppError; +use acari_lib::AcariError; pub enum OutputFormat { Pretty, @@ -21,12 +21,12 @@ pub enum OutputFormat { } impl OutputFormat { - pub fn from_string(format: &str) -> Result { + pub fn from_string(format: &str) -> Result { match format { "pretty" => Ok(OutputFormat::Pretty), "json" => Ok(OutputFormat::Json), "flat" => Ok(OutputFormat::Flat), - format => Err(AppError::UserError(format!("Invalid output format: {}", format))), + format => Err(AcariError::UserError(format!("Invalid output format: {}", format))), } } } diff --git a/cli/src/commands/projects.rs b/cli/src/commands/projects.rs index 5efbe78..96c099b 100644 --- a/cli/src/commands/projects.rs +++ b/cli/src/commands/projects.rs @@ -1,10 +1,9 @@ use super::OutputFormat; -use crate::error::AppError; -use acari_lib::{Client, Project}; +use acari_lib::{AcariError, Client, Project}; use itertools::Itertools; use prettytable::{cell, format, row, Table}; -pub fn all_projects(client: &dyn Client, output_format: OutputFormat) -> Result<(), AppError> { +pub fn all_projects(client: &dyn Client, output_format: OutputFormat) -> Result<(), AcariError> { let mut projects = client.get_projects()?; projects.sort_by(|p1, p2| p1.customer_name.cmp(&p2.customer_name)); @@ -36,7 +35,7 @@ fn print_pretty(projects: Vec<(&str, Vec<&Project>)>) { projects_table.printstd(); } -fn print_json(projects: Vec) -> Result<(), AppError> { +fn print_json(projects: Vec) -> Result<(), AcariError> { println!("{}", serde_json::to_string_pretty(&projects)?); Ok(()) diff --git a/cli/src/commands/services.rs b/cli/src/commands/services.rs index d1b8921..9ac081b 100644 --- a/cli/src/commands/services.rs +++ b/cli/src/commands/services.rs @@ -1,10 +1,9 @@ use super::OutputFormat; -use crate::error::AppError; -use acari_lib::{Client, Service}; +use acari_lib::{AcariError, Client, Service}; use itertools::Itertools; use prettytable::{cell, row, table}; -pub fn services(client: &dyn Client, output_format: OutputFormat) -> Result<(), AppError> { +pub fn services(client: &dyn Client, output_format: OutputFormat) -> Result<(), AcariError> { let mut services = client.get_services()?; services.sort_by(|s1, s2| s1.name.cmp(&s2.name)); @@ -28,7 +27,7 @@ fn print_pretty(services: Vec) { service_table.printstd(); } -fn print_json(services: Vec) -> Result<(), AppError> { +fn print_json(services: Vec) -> Result<(), AcariError> { println!("{}", serde_json::to_string_pretty(&services)?); Ok(()) diff --git a/cli/src/config.rs b/cli/src/config.rs index c434da4..0831c03 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -1,5 +1,4 @@ -use crate::error::AppError; -use acari_lib::{CachedClient, Client, StdClient}; +use acari_lib::{AcariError, CachedClient, Client, StdClient}; use serde::{Deserialize, Serialize}; use std::fs::{self, File}; use std::io::{self, Read, Write}; @@ -15,20 +14,22 @@ pub struct Config { } impl Config { - pub fn read() -> Result, AppError> { + pub fn read() -> Result, AcariError> { let config_file = config_file(); match File::open(&config_file) { Ok(mut file) => { let mut content = vec![]; file.read_to_end(&mut content)?; - Ok(Some(toml::from_slice::(&content)?)) + Ok(Some( + toml::from_slice::(&content).map_err(|e| AcariError::InternalError(format!("Configration invalid: {}", e)))?, + )) } Err(ref err) if err.kind() == io::ErrorKind::NotFound => Ok(None), Err(err) => Err(err.into()), } } - pub fn client(&self, cached: bool) -> Result, AppError> { + pub fn client(&self, cached: bool) -> Result, AcariError> { if cached { Ok(Box::new(CachedClient::new( &self.domain, @@ -40,11 +41,15 @@ impl Config { } } - pub fn write(&self) -> Result<(), AppError> { - let content = toml::to_string_pretty(self)?; + pub fn write(&self) -> Result<(), AcariError> { + let content = toml::to_string_pretty(self).map_err(|e| AcariError::InternalError(format!("Configration invalid: {}", e)))?; let config_file = config_file(); - fs::create_dir_all(&config_file.parent().ok_or_else(|| AppError::InternalError("Invalid config path".to_string()))?)?; + fs::create_dir_all( + &config_file + .parent() + .ok_or_else(|| AcariError::InternalError("Invalid config path".to_string()))?, + )?; let mut file = File::create(&config_file)?; diff --git a/cli/src/error.rs b/cli/src/error.rs deleted file mode 100644 index ff4d1de..0000000 --- a/cli/src/error.rs +++ /dev/null @@ -1,61 +0,0 @@ -use std::error::Error; -use std::fmt; -use std::io; - -#[derive(Debug)] -pub enum AppError { - IO(io::Error), - TextIO(text_io::Error), - TomlRead(toml::de::Error), - TomlWrite(toml::ser::Error), - Json(serde_json::Error), - AcariError(acari_lib::AcariError), - UserError(String), - InternalError(String), -} - -impl fmt::Display for AppError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - AppError::IO(err) => write!(f, "IO error: {}", err), - AppError::TextIO(err) => write!(f, "IO error: {}", err), - AppError::TomlRead(err) => write!(f, "Toml error: {}", err), - AppError::TomlWrite(err) => write!(f, "Toml error: {}", err), - AppError::Json(err) => write!(f, "Json error: {}", err), - AppError::AcariError(err) => write!(f, "Error: {}", err), - AppError::UserError(s) => write!(f, "User error: {}", s), - AppError::InternalError(s) => write!(f, "Internal error: {}", s), - } - } -} - -impl Error for AppError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - AppError::IO(err) => Some(err), - AppError::TextIO(err) => Some(err), - AppError::TomlRead(err) => Some(err), - AppError::TomlWrite(err) => Some(err), - AppError::Json(err) => Some(err), - AppError::AcariError(err) => Some(err), - _ => None, - } - } -} - -macro_rules! app_error_from { - ($error: ty, $app_error: ident) => { - impl From<$error> for AppError { - fn from(err: $error) -> AppError { - AppError::$app_error(err) - } - } - }; -} - -app_error_from!(io::Error, IO); -app_error_from!(text_io::Error, TextIO); -app_error_from!(toml::de::Error, TomlRead); -app_error_from!(toml::ser::Error, TomlWrite); -app_error_from!(serde_json::Error, Json); -app_error_from!(acari_lib::AcariError, AcariError); diff --git a/cli/src/main.rs b/cli/src/main.rs index 4b5ede9..b17a9a4 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,15 +1,13 @@ -use acari_lib::DateSpan; +use acari_lib::{AcariError, DateSpan}; use clap::{App, Arg, SubCommand}; mod commands; mod config; -mod error; use commands::OutputFormat; use config::Config; -use error::AppError; -fn main() -> Result<(), AppError> { +fn main() -> Result<(), AcariError> { let app = App::new("acarid") .version("0.1") .about("Commandline interface for mite") @@ -49,15 +47,17 @@ fn main() -> Result<(), AppError> { ("projects", _) => commands::all_projects(client.as_ref(), output_format), ("services", _) => commands::services(client.as_ref(), output_format), ("entries", Some(sub_matches)) => { - let span_arg = sub_matches.value_of("span").ok_or(AppError::UserError("Missing argument".to_string()))?; + let span_arg = sub_matches + .value_of("span") + .ok_or(AcariError::UserError("Missing argument".to_string()))?; commands::entries(client.as_ref(), output_format, DateSpan::from_string(span_arg)?) } - (invalid, _) => Err(AppError::UserError(format!("Unknown command: {}", invalid))), + (invalid, _) => Err(AcariError::UserError(format!("Unknown command: {}", invalid))), } } None => match matches.subcommand() { ("init", _) => commands::init(), - (_, _) => Err(AppError::UserError("Missing configuration, run init first".to_string())), + (_, _) => Err(AcariError::UserError("Missing configuration, run init first".to_string())), }, } } diff --git a/lib/src/error.rs b/lib/src/error.rs index 346cadd..557f0b2 100644 --- a/lib/src/error.rs +++ b/lib/src/error.rs @@ -10,6 +10,8 @@ pub enum AcariError { Request(reqwest::Error), Json(serde_json::Error), Mite(u16, String), + UserError(String), + InternalError(String), } impl fmt::Display for AcariError { @@ -21,6 +23,8 @@ impl fmt::Display for AcariError { AcariError::Request(err) => write!(f, "Request error: {}", err), AcariError::Json(err) => write!(f, "Json error: {}", err), AcariError::Mite(status, error) => write!(f, "Mite error ({}): {}", status, error), + AcariError::UserError(s) => write!(f, "User error: {}", s), + AcariError::InternalError(s) => write!(f, "Internal error: {}", s), } } }