#![allow(irrefutable_let_patterns)]
use std::path::PathBuf;
use std::{fmt, io};
use std::fmt::{Display, Formatter};
use std::io::ErrorKind;
use crate::{log_error, log_warn};
pub type Result<T> = std::result::Result<T, Error>;
#[repr(i32)]
#[derive(Debug)]
pub enum Error {
IOError(IOErrorKind),
OSError(OSErrorKind),
InvalidData(InvalidDataKind),
InvalidInput(InvalidInputKind),
EncryptionError(EncryptionErrorKind),
SerializeError(SerializeErrorKind),
ProfileError(ProfileErrorKind),
ConfigError(ConfigErrorKind),
}
impl Error {
pub fn should_exit(&self) -> bool {
match self {
Error::IOError(_) => true,
Error::OSError(_) => true,
Error::InvalidData(_) => true,
Error::InvalidInput(_) => true,
Error::EncryptionError(_) => true,
Error::SerializeError(_) => true,
Error::ProfileError(_) => true,
Error::ConfigError(_) => true,
}
}
pub fn exit_code(&self) -> i32 {
match self {
Error::IOError(_) => 1,
Error::OSError(_) => 2,
Error::InvalidData(_) => 3,
Error::InvalidInput(_) => 4,
Error::EncryptionError(_) => 5,
Error::SerializeError(_) => 6,
Error::ProfileError(_) => 7,
Error::ConfigError(_) => 8,
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Error::IOError(e) => write!(f, "IO error: {}", e),
Error::OSError(e) => write!(f, "OS error: {}", e),
Error::InvalidData(e) => write!(f, "Invalid data: {}", e),
Error::InvalidInput(e) => write!(f, "Invalid input provided: {}", e),
Error::EncryptionError(e) => write!(f, "Encryption error: {}", e),
Error::SerializeError(e) => write!(f, "Serialization error: {}", e),
Error::ProfileError(e) => write!(f, "Profile error: {}", e),
Error::ConfigError(e) => write!(f, "Configuration error: {}", e),
}
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Error::IOError(IOErrorKind::StdError(
IOErrorContainer {
kind: err.kind(),
message: err.to_string(),
}
))
}
}
impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Self {
Error::SerializeError(SerializeErrorKind::JSONParseError(err.to_string(), err.line(), err.column()))
}
}
impl From<toml::ser::Error> for Error {
fn from(err: toml::ser::Error) -> Self {
Error::SerializeError(SerializeErrorKind::TOMLParseError(err.to_string()))
}
}
impl From<toml::de::Error> for Error {
fn from(err: toml::de::Error) -> Self {
Error::SerializeError(SerializeErrorKind::TOMLParseError(err.to_string()))
}
}
#[derive(Debug)]
pub enum IOErrorKind {
StdError(IOErrorContainer),
NotFound(PathBuf),
NotSupported(PathBuf),
}
impl Display for IOErrorKind {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
IOErrorKind::StdError(e) => write!(f, "{} - {}", e.kind, e.message),
IOErrorKind::NotFound(p) => write!(f, "File \"{}\" not found", p.display()),
IOErrorKind::NotSupported(p) => write!(f, "File \"{}\" is not supported", p.display()),
}
}
}
#[derive(Debug)]
pub struct IOErrorContainer {
pub kind: ErrorKind,
pub message: String,
}
#[derive(Debug)]
pub enum OSErrorKind {
EnvVariableUnavailable(String),
}
impl Display for OSErrorKind {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
OSErrorKind::EnvVariableUnavailable(s) => write!(f, "Unable to get the \"{}\" environment variable", s),
}
}
}
#[derive(Debug)]
pub enum InvalidDataKind {
InvalidHex(String),
InvalidLength(String),
MissingData(String),
}
impl Display for InvalidDataKind {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
InvalidDataKind::InvalidHex(s) => write!(f, "Invalid hex number ({})", s),
InvalidDataKind::InvalidLength(s) => write!(f, "Invalid length of {}", s),
InvalidDataKind::MissingData(s) => write!(f, "Some data is missing ({})", s),
}
}
}
#[derive(Debug)]
pub enum InvalidInputKind {
InvalidFile(String),
}
impl Display for InvalidInputKind {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
InvalidInputKind::InvalidFile(s) => write!(f, "Invalid file provided ({})", s),
}
}
}
#[derive(Debug)]
pub enum EncryptionErrorKind {
CipherError(String),
HashError(String),
}
impl Display for EncryptionErrorKind {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
EncryptionErrorKind::CipherError(s) => write!(f, "Unable to apply cipher ({})", s),
EncryptionErrorKind::HashError(s) => write!(f, "Unable to generate a hash ({})", s),
}
}
}
#[derive(Debug)]
pub enum SerializeErrorKind {
JSONParseError(String, usize, usize),
TOMLParseError(String),
BoxfileParseError(String),
HeaderParseError(String),
}
impl Display for SerializeErrorKind {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
SerializeErrorKind::JSONParseError(s, l, c) => write!(f, "Unable to parse a JSON file (line {}, column {}):\n{}", l, c, s),
SerializeErrorKind::TOMLParseError(s) => write!(f, "Unable to parse a TOML file:\n{}", s),
SerializeErrorKind::BoxfileParseError(s) => write!(f, "Unable to parse a boxfile:\n{}", s),
SerializeErrorKind::HeaderParseError(s) => write!(f, "Unable to parse a boxfile header:\n{}", s),
}
}
}
#[derive(Debug)]
pub enum ProfileErrorKind {
NotFound(String),
NotSelected,
AlreadySelected(String),
AlreadyExists(String),
AuthenticationFailed,
MismatchedProfile,
}
impl Display for ProfileErrorKind {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
ProfileErrorKind::NotFound(s) => write!(f, "Profile \"{}\" not found", s),
ProfileErrorKind::NotSelected => write!(f, "No profile is currently selected"),
ProfileErrorKind::AlreadySelected(s) => write!(f, "Profile \"{}\" is already selected", s),
ProfileErrorKind::AlreadyExists(s) => write!(f, "Profile \"{}\" already exists", s),
ProfileErrorKind::AuthenticationFailed => write!(f, "Authentication failed. Invalid profile password provided"),
ProfileErrorKind::MismatchedProfile => write!(f, "Mismatched profile. File seems to be encrypted with a different one."),
}
}
}
#[derive(Debug)]
pub enum ConfigErrorKind {}
impl Display for ConfigErrorKind {
fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result {
todo!()
}
}
pub fn print_error(err: &Error) {
log_error!("{}", err);
match &err {
Error::ProfileError(kind) => {
if let ProfileErrorKind::AuthenticationFailed = kind {
log_warn!("Try again or use a different profile")
} else {
log_warn!("New profile can be created with \"databoxer profile new\"");
}
},
Error::ConfigError(_) => {
log_warn!("Please check the config file for any mistakes and try again");
}
_ => {}
}
}
#[macro_export]
macro_rules! new_err {
($err:ident: $kind:ident) => {
{
use paste::paste;
paste! {
use crate::core::error::{Error, [<$err Kind>]};
Error::$err([<$err Kind>]::$kind)
}
}
};
($err:ident: $kind:ident, $msg:expr) => {
{
use paste::paste;
paste! {
use crate::core::error::{Error, [<$err Kind>]};
Error::$err([<$err Kind>]::$kind($msg.to_string()))
}
}
};
}
#[macro_export]
macro_rules! err_cmp {
($err:expr, $err_type:ident) => {
{
use crate::core::error::Error;
if let Error::$err_type(_) = &$err {
true
} else {
false
}
}
};
($err:expr, $err_type:ident, $err_kind:ident) => {
{
use paste::paste;
use crate::core::error::Error;
if let Error::$err_type(kind) = &$err {
paste! {
use crate::core::error::[<$err_type Kind>];
if let [<$err_type Kind>]::$err_kind = kind {
true
} else {
false
}
}
} else {
false
}
}
};
($err:expr, $err_type:ident, $err_kind:ident()) => {
{
use paste::paste;
use crate::core::error::Error;
if let Error::$err_type(kind) = &$err {
paste! {
use crate::core::error::[<$err_type Kind>];
if let [<$err_type Kind>]::$err_kind(_) = kind {
true
} else {
false
}
}
} else {
false
}
}
};
}
#[macro_export]
macro_rules! exits_on {
($err:expr; default) => {
use crate::core::error::print_error;
print_error(&$err);
if $err.should_exit() {
std::process::exit($err.exit_code());
}
};
($err:expr; all) => {
use crate::core::error::print_error;
print_error(&$err);
std::process::exit($err.exit_code());
};
($err:expr; $($err_kind:ident),*) => {
use crate::core::error::{Error, print_error};
print_error(&$err);
match $err {
$(
Error::$err_kind(_) => std::process::exit($err.exit_code());
),*
_ => {}
}
};
($err:expr; $($err_kind:ident $should:expr);*) => {
use crate::core::error::{Error, print_error};
print_error(&$err);
match $err {
$(
Error::$err_kind(_) => {
if $should {
std::process::exit($err.exit_code());
}
}
),*
_ => {
if $err.should_exit() {
std::process::exit($err.exit_code());
}
}
}
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_err_cmp() {
let err = Error::ProfileError(ProfileErrorKind::AuthenticationFailed);
let compare_type = err_cmp!(err, ProfileError);
assert!(compare_type);
let compare_kind = err_cmp!(err, ProfileError, AuthenticationFailed);
assert!(compare_kind);
let failed_compare_type = err_cmp!(err, OSError);
assert_ne!(failed_compare_type, true);
let failed_compare_kind = err_cmp!(err, ProfileError, MismatchedProfile);
assert_ne!(failed_compare_kind, true);
}
#[test]
fn test_new_err_macro() {
let err = new_err!(ProfileError: NotSelected);
assert!(err_cmp!(err, ProfileError, NotSelected));
let str_err = new_err!(InvalidData: InvalidHex, "placeholder");
assert!(err_cmp!(str_err, InvalidData, InvalidHex()));
}
}