1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use std::{fmt, path::PathBuf};

use crate::{oof, utils::colors};

pub enum Error {
    UnknownExtensionError(String),
    MissingExtensionError(PathBuf),
    // TODO: get rid of this error variant
    InvalidUnicode,
    InvalidInput,
    IoError(std::io::Error),
    FileNotFound(PathBuf),
    AlreadyExists,
    InvalidZipArchive(&'static str),
    PermissionDenied,
    UnsupportedZipArchive(&'static str),
    InternalError,
    OofError,
    CompressingRootFolder,
    MissingArgumentsForCompression,
    CompressionTypo,
    WalkdirError,
}

pub type Result<T> = std::result::Result<T, Error>;

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Error::MissingExtensionError(filename) => {
                write!(f, "{}[ERROR]{} ", colors::red(), colors::reset())?;
                // TODO: show MIME type of the unsupported file
                write!(f, "cannot compress to {:?}, likely because it has an unsupported (or missing) extension.", filename)
            },
            Error::WalkdirError => {
                // Already printed in the From block
                write!(f, "")
            },
            Error::FileNotFound(file) => {
                write!(f, "{}[ERROR]{} ", colors::red(), colors::reset())?;
                if file == &PathBuf::from("") {
                    return write!(f, "file not found!");
                }
                write!(f, "file {:?} not found!", file)
            },
            Error::CompressingRootFolder => {
                write!(f, "{}[ERROR]{} ", colors::red(), colors::reset())?;
                let spacing = "        ";
                writeln!(f, "It seems you're trying to compress the root folder.")?;
                writeln!(
                    f,
                    "{}This is unadvisable since ouch does compressions in-memory.",
                    spacing
                )?;
                write!(
                    f,
                    "{}Use a more appropriate tool for this, such as {}rsync{}.",
                    spacing,
                    colors::green(),
                    colors::reset()
                )
            },
            Error::MissingArgumentsForCompression => {
                write!(f, "{}[ERROR]{} ", colors::red(), colors::reset())?;
                let spacing = "        ";
                writeln!(f,"The compress subcommands demands at least 2 arguments, an input file and an output file.")?;
                writeln!(f, "{}Example: `ouch compress img.jpeg img.zip`", spacing)?;
                write!(f, "{}For more information, run `ouch --help`", spacing)
            },
            Error::InternalError => {
                write!(f, "{}[ERROR]{} ", colors::red(), colors::reset())?;
                write!(f, "You've reached an internal error! This really should not have happened.\nPlease file an issue at {}https://github.com/vrmiguel/ouch{}", colors::green(), colors::reset())
            },
            Error::IoError(io_err) => {
                write!(f, "{}[ERROR]{} {}", colors::red(), colors::reset(), io_err)
            },
            Error::CompressionTypo => {
                write!(f, "Did you mean {}ouch compress{}?", colors::magenta(), colors::reset())
            },
            _err => {
                todo!();
            },
        }
    }
}

impl From<std::io::Error> for Error {
    fn from(err: std::io::Error) -> Self {
        match err.kind() {
            std::io::ErrorKind::NotFound => panic!("{}", err),
            std::io::ErrorKind::PermissionDenied => Self::PermissionDenied,
            std::io::ErrorKind::AlreadyExists => Self::AlreadyExists,
            _other => Self::IoError(err),
        }
    }
}

impl From<zip::result::ZipError> for Error {
    fn from(err: zip::result::ZipError) -> Self {
        use zip::result::ZipError::*;
        match err {
            Io(io_err) => Self::from(io_err),
            InvalidArchive(filename) => Self::InvalidZipArchive(filename),
            FileNotFound => Self::FileNotFound("".into()),
            UnsupportedArchive(filename) => Self::UnsupportedZipArchive(filename),
        }
    }
}

impl From<walkdir::Error> for Error {
    fn from(err: walkdir::Error) -> Self {
        eprintln!("{}[ERROR]{} {}", colors::red(), colors::reset(), err);
        Self::WalkdirError
    }
}

impl From<oof::OofError> for Error {
    fn from(err: oof::OofError) -> Self {
        // To avoid entering a lifetime hell, we'll just print the Oof error here
        // and skip saving it into a variant of Self
        println!("{}[ERROR]{} {}", colors::red(), colors::reset(), err);
        Self::OofError
    }
}