Skip to main content

sit/
error.rs

1use crate::structs::{Algorithm, Version};
2use fourcc::FourCC;
3use std::{fmt, io};
4
5#[derive(Debug)]
6/// Reasons why files can be ruled out as valid StuffIt archives
7pub enum InvalidFileReason {
8    InvalidHeader,
9    CreatorCode(FourCC),
10    FileMagic(FourCC),
11}
12
13impl fmt::Display for InvalidFileReason {
14    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15        match self {
16            InvalidFileReason::CreatorCode(code) => f.write_fmt(format_args!(
17                "Creator code is '{}' instead of 'rLau'",
18                code.name()
19            )),
20            InvalidFileReason::FileMagic(code) => f.write_fmt(format_args!(
21                "File code is '{}' but should be one of 'SIT!', 'SITD', 'SIT2' or 'SIT5'",
22                code.name()
23            )),
24            InvalidFileReason::InvalidHeader => {
25                f.write_str("Could not find valid archive header at the given position")
26            }
27        }
28    }
29}
30
31#[derive(Debug)]
32/// Archive features that are known to exist but are not implemented yet
33pub enum UnsupportedFeature {
34    Algorithm(Algorithm),
35    Version(Version),
36    Encryption,
37}
38
39impl fmt::Display for UnsupportedFeature {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        match self {
42            UnsupportedFeature::Algorithm(algo) => {
43                f.write_fmt(format_args!("Algorithm {algo} is not implemented yet"))
44            }
45            UnsupportedFeature::Version(version) => {
46                f.write_fmt(format_args!("The version {version} is not implemented yet"))
47            }
48            UnsupportedFeature::Encryption => f.write_str("Encryption is not implemented yet"),
49        }
50    }
51}
52
53impl From<Algorithm> for UnsupportedFeature {
54    fn from(val: Algorithm) -> Self {
55        UnsupportedFeature::Algorithm(val)
56    }
57}
58
59impl From<Version> for UnsupportedFeature {
60    fn from(val: Version) -> Self {
61        UnsupportedFeature::Version(val)
62    }
63}
64
65#[derive(Debug)]
66pub enum ChecksumLocation {
67    /// Checksum for the archive header is invalid
68    ArchiveHeader,
69    /// Checksum for an entry header does not match
70    EntryHeader,
71    /// Checksum for an entry's data is corrupt
72    DataFork,
73    /// Checksum for an entry's resource fork is corrupt
74    ResourceFork,
75    DataStream,
76}
77
78impl fmt::Display for ChecksumLocation {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        match self {
81            ChecksumLocation::ArchiveHeader => f.write_str("archive header"),
82            ChecksumLocation::EntryHeader => f.write_str("entry header"),
83            ChecksumLocation::DataFork => f.write_str("data fork"),
84            ChecksumLocation::ResourceFork => f.write_str("resource fork"),
85            ChecksumLocation::DataStream => f.write_str("data stream"),
86        }
87    }
88}
89
90#[derive(Debug, thiserror::Error)]
91/// Common error for working with the sit crate
92pub enum Error {
93    #[error(transparent)]
94    Io(io::Error),
95
96    #[error(transparent)]
97    BinRW(#[from] binrw::Error),
98
99    #[error("The file is not a Stuffit archive ({0})")]
100    InvalidFile(InvalidFileReason),
101
102    #[error("The entry uses unsupported features ({0})")]
103    UnsupportedFeature(UnsupportedFeature),
104
105    #[error("One or more checksums indicate archive corruption")]
106    ChecksumMismatch(ChecksumLocation),
107
108    #[error("The compressed data stream could not read")]
109    InvalidDataStream,
110}
111
112impl From<Error> for binrw::Error {
113    fn from(val: Error) -> Self {
114        match val {
115            Error::Io(error) => binrw::Error::Io(error),
116            Error::BinRW(error) => error,
117            err => binrw::Error::Custom {
118                pos: 0,
119                err: Box::new(err),
120            },
121        }
122    }
123}
124
125impl From<Error> for io::Error {
126    fn from(val: Error) -> Self {
127        match val {
128            Error::Io(error) => error,
129            Error::BinRW(error) => io::Error::other(Box::new(error)),
130            err => io::Error::other(Box::new(err)),
131        }
132    }
133}
134
135impl From<crate::algos::huffman::Error> for Error {
136    fn from(_: crate::algos::huffman::Error) -> Self {
137        Self::InvalidDataStream
138    }
139}
140
141impl From<crate::algos::huffman_fixed::Error> for Error {
142    fn from(_: crate::algos::huffman_fixed::Error) -> Self {
143        Self::InvalidDataStream
144    }
145}
146
147impl From<crate::algos::arsenic::Error> for Error {
148    fn from(_: crate::algos::arsenic::Error) -> Self {
149        Self::InvalidDataStream
150    }
151}
152
153impl From<io::Error> for Error {
154    fn from(e: io::Error) -> Self {
155        match e {
156            e if e.kind() == io::ErrorKind::Other => match e.downcast() {
157                Ok(e) => e,
158                Err(e) => Error::Io(e),
159            },
160            e => Error::Io(e),
161        }
162    }
163}
164
165#[derive(thiserror::Error, Debug)]
166/// Error when trying to extract a specifc item form the archive
167pub enum ExtractionError {
168    #[error("The requested entry does not exist in the sit archive")]
169    ItemNotFound,
170    #[error(transparent)]
171    Error(#[from] Error),
172}
173
174impl From<io::Error> for ExtractionError {
175    fn from(value: io::Error) -> Self {
176        Self::Error(value.into())
177    }
178}
179
180impl From<ExtractionError> for io::Error {
181    fn from(value: ExtractionError) -> Self {
182        match value {
183            ExtractionError::ItemNotFound => {
184                io::Error::other("Requested file was not found in the archive")
185            }
186            other => other.into(),
187        }
188    }
189}