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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
use crate::huffman::HuffmanError;
use bitreader::BitReaderError;
use std::array::TryFromSliceError;
use std::error::Error;
use std::ffi::FromBytesWithNulError;
use std::fmt::Display;
use std::io::ErrorKind;
use std::str::Utf8Error;

/// Error types that may occur when reading a CHD file or hunk.
///
/// This type tries to be ABI-compatible with [libchdr](https://github.com/rtissera/libchdr/blob/6eeb6abc4adc094d489c8ba8cafdcff9ff61251b/include/libchdr/chd.h#L258),
/// given sane defaults in the C compiler. See [repr(C) in the Rustonomicon](https://doc.rust-lang.org/nomicon/other-reprs.html#reprc) for more details.
#[derive(Debug)]
#[repr(C)]
pub enum ChdError {
    None,
    NoInterface,
    OutOfMemory,
    InvalidFile,
    InvalidParameter,
    InvalidData,
    FileNotFound,
    RequiresParent,
    FileNotWriteable,
    ReadError,
    WriteError,
    CodecError,
    InvalidParent,
    HunkOutOfRange,
    DecompressionError,
    CompressionError,
    CantCreateFile,
    CantVerify,
    NotSupported,
    MetadataNotFound,
    InvalidMetadataSize,
    UnsupportedVersion,
    VerifyIncomplete,
    InvalidMetadata,
    InvalidState,
    OperationPending,
    NoAsyncOperation,
    UnsupportedFormat,
    Unknown,
}

impl Error for ChdError {}

impl Display for ChdError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ChdError::None => f.write_str("no error"),
            ChdError::NoInterface => f.write_str("no drive interface"),
            ChdError::OutOfMemory => f.write_str("out of memory"),
            ChdError::InvalidFile => f.write_str("invalid file"),
            ChdError::InvalidParameter => f.write_str("invalid parameter"),
            ChdError::InvalidData => f.write_str("invalid data"),
            ChdError::FileNotFound => f.write_str("file not found"),
            ChdError::RequiresParent => f.write_str("requires parent"),
            ChdError::FileNotWriteable => f.write_str("file not writeable"),
            ChdError::ReadError => f.write_str("read error"),
            ChdError::WriteError => f.write_str("write error"),
            ChdError::CodecError => f.write_str("codec error"),
            ChdError::InvalidParent => f.write_str("invalid parent"),
            ChdError::HunkOutOfRange => f.write_str("hunk out of range"),
            ChdError::DecompressionError => f.write_str("decompression error"),
            ChdError::CompressionError => f.write_str("compression error"),
            ChdError::CantCreateFile => f.write_str("can't create file"),
            ChdError::CantVerify => f.write_str("can't verify file"),
            ChdError::NotSupported => f.write_str("operation not supported"),
            ChdError::MetadataNotFound => f.write_str("can't find metadata"),
            ChdError::InvalidMetadataSize => f.write_str("invalid metadata size"),
            ChdError::UnsupportedVersion => f.write_str("unsupported CHD version"),
            ChdError::VerifyIncomplete => f.write_str("incomplete verify"),
            ChdError::InvalidMetadata => f.write_str("invalid metadata"),
            ChdError::InvalidState => f.write_str("invalid state"),
            ChdError::OperationPending => f.write_str("operation pending"),
            ChdError::NoAsyncOperation => f.write_str("no async operation in progress"),
            ChdError::UnsupportedFormat => f.write_str("unsupported format"),
            ChdError::Unknown => f.write_str("undocumented error"),
        }
    }
}

impl From<TryFromSliceError> for ChdError {
    fn from(_: TryFromSliceError) -> Self {
        return ChdError::InvalidFile;
    }
}

impl From<BitReaderError> for ChdError {
    fn from(_: BitReaderError) -> Self {
        return ChdError::ReadError;
    }
}

impl From<FromBytesWithNulError> for ChdError {
    fn from(_: FromBytesWithNulError) -> Self {
        return ChdError::InvalidData;
    }
}

impl From<Utf8Error> for ChdError {
    fn from(_: Utf8Error) -> Self {
        return ChdError::InvalidData;
    }
}

impl From<std::io::Error> for ChdError {
    fn from(err: std::io::Error) -> Self {
        match err.kind() {
            ErrorKind::NotFound => ChdError::FileNotFound,
            ErrorKind::PermissionDenied => ChdError::NotSupported,
            ErrorKind::ConnectionRefused => ChdError::Unknown,
            ErrorKind::ConnectionReset => ChdError::Unknown,
            ErrorKind::ConnectionAborted => ChdError::Unknown,
            ErrorKind::NotConnected => ChdError::Unknown,
            ErrorKind::AddrInUse => ChdError::Unknown,
            ErrorKind::AddrNotAvailable => ChdError::Unknown,
            ErrorKind::BrokenPipe => ChdError::Unknown,
            ErrorKind::AlreadyExists => ChdError::CantCreateFile,
            ErrorKind::WouldBlock => ChdError::Unknown,
            ErrorKind::InvalidInput => ChdError::InvalidParameter,
            ErrorKind::InvalidData => ChdError::InvalidData,
            ErrorKind::TimedOut => ChdError::Unknown,
            ErrorKind::WriteZero => ChdError::WriteError,
            ErrorKind::Interrupted => ChdError::Unknown,
            ErrorKind::Other => ChdError::Unknown,
            ErrorKind::UnexpectedEof => ChdError::ReadError,
            ErrorKind::Unsupported => ChdError::NotSupported,
            ErrorKind::OutOfMemory => ChdError::OutOfMemory,
            _ => ChdError::Unknown,
        }
    }
}

impl From<HuffmanError> for ChdError {
    fn from(_: HuffmanError) -> Self {
        ChdError::DecompressionError
    }
}

impl From<ChdError> for std::io::Error {
    fn from(e: ChdError) -> Self {
        std::io::Error::new(ErrorKind::Other, e)
    }
}

/// Result type for `chd`.
pub type Result<T> = std::result::Result<T, ChdError>;