mirror of
https://github.com/gosticks/acari.git
synced 2026-06-29 05:50:02 +00:00
One type of error should be enough
This commit is contained in:
@@ -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!({
|
||||
|
||||
@@ -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<Customer>) {
|
||||
customers_table.printstd();
|
||||
}
|
||||
|
||||
fn print_json(customers: Vec<Customer>) -> Result<(), AppError> {
|
||||
fn print_json(customers: Vec<Customer>) -> Result<(), AcariError> {
|
||||
println!("{}", serde_json::to_string_pretty(&customers)?);
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -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<TimeEntry>) -> Result<(), AppError> {
|
||||
fn print_json(entries: Vec<TimeEntry>) -> Result<(), AcariError> {
|
||||
println!("{}", serde_json::to_string_pretty(&entries)?);
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<OutputFormat, AppError> {
|
||||
pub fn from_string(format: &str) -> Result<OutputFormat, AcariError> {
|
||||
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))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Project>) -> Result<(), AppError> {
|
||||
fn print_json(projects: Vec<Project>) -> Result<(), AcariError> {
|
||||
println!("{}", serde_json::to_string_pretty(&projects)?);
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -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>) {
|
||||
service_table.printstd();
|
||||
}
|
||||
|
||||
fn print_json(services: Vec<Service>) -> Result<(), AppError> {
|
||||
fn print_json(services: Vec<Service>) -> Result<(), AcariError> {
|
||||
println!("{}", serde_json::to_string_pretty(&services)?);
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -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<Option<Config>, AppError> {
|
||||
pub fn read() -> Result<Option<Config>, 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::<Config>(&content)?))
|
||||
Ok(Some(
|
||||
toml::from_slice::<Config>(&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<Box<dyn Client>, AppError> {
|
||||
pub fn client(&self, cached: bool) -> Result<Box<dyn Client>, 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)?;
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -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 <span> argument".to_string()))?;
|
||||
let span_arg = sub_matches
|
||||
.value_of("span")
|
||||
.ok_or(AcariError::UserError("Missing <span> 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())),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user