use std::fmt;
use vkteams_bot::error::BotError;
pub static FILE_NOT_FOUND: &str = "File not found: ";
pub static DIR_NOT_FOUND: &str = "Directory not found: ";
pub static NOT_A_FILE: &str = "Path is not a file: ";
pub static NOT_A_DIR: &str = "Path is not a directory: ";
pub static WRITE_ERROR: &str = "Failed to write file: ";
pub static READ_ERROR: &str = "Failed to read file: ";
pub static DOWNLOAD_ERROR: &str = "Failed to download file: ";
pub static API_ERROR: &str = "API Error: ";
pub static INPUT_ERROR: &str = "Input Error: ";
pub static UNEXPECTED_ERROR: &str = "Unexpected Error: ";
#[derive(Debug)]
pub enum CliError {
ApiError(BotError),
FileError(String),
InputError(String),
UnexpectedError(String),
Storage(String),
Config(String),
DaemonAlreadyRunning,
DaemonNotRunning,
System(String),
}
pub type Result<T> = std::result::Result<T, CliError>;
impl From<BotError> for CliError {
fn from(error: BotError) -> Self {
CliError::ApiError(error)
}
}
impl From<std::io::Error> for CliError {
fn from(error: std::io::Error) -> Self {
CliError::FileError(error.to_string())
}
}
impl From<serde_json::Error> for CliError {
fn from(error: serde_json::Error) -> Self {
CliError::UnexpectedError(format!("JSON error: {error}"))
}
}
impl fmt::Display for CliError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CliError::ApiError(err) => write!(f, "{API_ERROR}{err}"),
CliError::FileError(err) => write!(f, "File Error: {err}"),
CliError::InputError(err) => write!(f, "{INPUT_ERROR}{err}"),
CliError::UnexpectedError(err) => write!(f, "{UNEXPECTED_ERROR}{err}"),
CliError::Storage(err) => write!(f, "Storage Error: {err}"),
CliError::Config(err) => write!(f, "Configuration Error: {err}"),
CliError::DaemonAlreadyRunning => write!(f, "Daemon is already running"),
CliError::DaemonNotRunning => write!(f, "Daemon is not running"),
CliError::System(err) => write!(f, "System Error: {err}"),
}
}
}
impl std::error::Error for CliError {}
impl CliError {
#[must_use]
pub fn exit_code(&self) -> i32 {
match self {
CliError::ApiError(_) => exitcode::UNAVAILABLE,
CliError::FileError(_) => exitcode::IOERR,
CliError::InputError(_) => exitcode::USAGE,
CliError::UnexpectedError(_) => exitcode::SOFTWARE,
CliError::Storage(_) => exitcode::IOERR,
CliError::Config(_) => exitcode::CONFIG,
CliError::DaemonAlreadyRunning => exitcode::TEMPFAIL,
CliError::DaemonNotRunning => exitcode::TEMPFAIL,
CliError::System(_) => exitcode::OSERR,
}
}
}
pub mod prelude {
pub use super::{
API_ERROR, DIR_NOT_FOUND, DOWNLOAD_ERROR, FILE_NOT_FOUND, INPUT_ERROR, NOT_A_DIR,
NOT_A_FILE, READ_ERROR, UNEXPECTED_ERROR, WRITE_ERROR,
};
pub use super::{CliError, Result};
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json;
use std::io;
use vkteams_bot::error::BotError;
#[test]
fn test_cli_error_display_and_exit_code() {
let api_err = CliError::ApiError(BotError::Api(vkteams_bot::error::ApiError {
description: "api fail".to_string(),
}));
assert!(format!("{api_err}").contains("API Error:"));
assert_eq!(api_err.exit_code(), exitcode::UNAVAILABLE);
let file_err = CliError::FileError("file fail".to_string());
assert!(format!("{file_err}").contains("File Error:"));
assert_eq!(file_err.exit_code(), exitcode::IOERR);
let input_err = CliError::InputError("bad arg".to_string());
assert!(format!("{input_err}").contains("Input Error:"));
assert_eq!(input_err.exit_code(), exitcode::USAGE);
let unexp_err = CliError::UnexpectedError("boom".to_string());
assert!(format!("{unexp_err}").contains("Unexpected Error:"));
assert_eq!(unexp_err.exit_code(), exitcode::SOFTWARE);
}
#[test]
fn test_from_bot_error() {
let bot_err = BotError::Api(vkteams_bot::error::ApiError {
description: "api fail".to_string(),
});
let cli_err: CliError = bot_err.into();
match cli_err {
CliError::ApiError(_) => {}
_ => panic!("Expected ApiError variant"),
}
}
#[test]
fn test_from_io_error() {
let io_err = io::Error::other("io fail");
let cli_err: CliError = io_err.into();
match cli_err {
CliError::FileError(msg) => assert!(msg.contains("io fail")),
_ => panic!("Expected FileError variant"),
}
}
#[test]
fn test_from_serde_json_error() {
let json_err = serde_json::from_str::<u32>("not a number").unwrap_err();
let cli_err: CliError = json_err.into();
match cli_err {
CliError::UnexpectedError(msg) => assert!(msg.contains("JSON error")),
_ => panic!("Expected UnexpectedError variant"),
}
}
}