lib_epub/
error.rs

1//! Error Type Definition Module
2//!
3//! This module defines the various error types that may be encountered during
4//! EPUB file parsing and processing. All errors are uniformly wrapped in the
5//! `EpubError` enumeration for convenient error handling by the caller.
6//!
7//! ## Main Error Types
8//!
9//! - [EpubError] - Enumeration of main errors during EPUB processing
10//! - [EpubBuilderError] - Specific errors during EPUB build process (requires `builder` functionality enabled)
11
12use thiserror::Error;
13
14/// Types of errors that can occur during EPUB processing
15///
16/// This enumeration defines the various error cases that can be encountered
17/// when parsing and processing EPUB files, including file format errors,
18/// missing resources, compression issues, etc.
19#[derive(Debug, Error)]
20pub enum EpubError {
21    /// ZIP archive related errors
22    ///
23    /// Errors occur when processing the ZIP structure of EPUB files,
24    /// such as file corruption, unreadability, etc.
25    #[error("Archive error: {source}")]
26    ArchiveError { source: zip::result::ZipError },
27
28    /// Data Decoding Error - Null dataw
29    ///
30    /// This error occurs when trying to decode an empty stream.
31    #[error("Decode error: The data is empty.")]
32    EmptyDataError,
33
34    #[cfg(feature = "builder")]
35    #[error("Epub builder error: {source}")]
36    EpubBuilderError { source: EpubBuilderError },
37
38    /// XML parsing failure error
39    ///
40    /// This error usually only occurs when there is an exception in the XML parsing process,
41    /// the event listener ends abnormally, resulting in the root node not being initialized.
42    /// This exception may be caused by an incorrect XML file.
43    #[error(
44        "Failed parsing XML error: Unknown problems occurred during XML parsing, causing parsing failure."
45    )]
46    FailedParsingXml,
47
48    #[error("IO error: {source}")]
49    IOError { source: std::io::Error },
50
51    /// Missing required attribute error
52    ///
53    /// Triggered when an XML element in an EPUB file lacks the required
54    /// attributes required by the EPUB specification.
55    #[error(
56        "Missing required attribute: The \"{attribute}\" attribute is a must attribute for the \"{tag}\" element."
57    )]
58    MissingRequiredAttribute { tag: String, attribute: String },
59
60    /// Non-canonical EPUB structure error
61    ///
62    /// This error occurs when an EPUB file lacks some files or directory
63    /// structure that is required in EPUB specification.
64    #[error("Non-canonical epub: The \"{expected_file}\" file was not found.")]
65    NonCanonicalEpub { expected_file: String },
66
67    /// Non-canonical file structure error
68    ///
69    /// This error is triggered when the required XML elements in the
70    /// specification are missing from the EPUB file.
71    #[error("Non-canonical file: The \"{tag}\" elements was not found.")]
72    NonCanonicalFile { tag: String },
73
74    /// Missing supported file format error
75    ///
76    /// This error occurs when trying to get a resource but there isn't any supported file format.
77    /// It usually happens when there are no supported formats available in the fallback chain.
78    #[error(
79        "No supported file format: The fallback resource does not contain the file format you support."
80    )]
81    NoSupportedFileFormat,
82
83    /// Relative link leak error
84    ///
85    /// This error occurs when a relative path link is outside the scope
86    /// of an EPUB container, which is a security protection mechanism.
87    #[error("Relative link leakage: Path \"{path}\" is out of container range.")]
88    RealtiveLinkLeakage { path: String },
89
90    /// Unable to find the resource id error
91    ///
92    /// This error occurs when trying to get a resource by id but that id doesn't exist in the manifest.
93    #[error("Resource Id Not Exist: There is no resource item with id \"{id}\".")]
94    ResourceIdNotExist { id: String },
95
96    /// Unable to find the resource error
97    ///
98    /// This error occurs when an attempt is made to get a resource
99    /// but it does not exist in the EPUB container.
100    #[error("Resource not found: Unable to find resource from \"{resource}\".")]
101    ResourceNotFound { resource: String },
102
103    /// Unrecognized EPUB version error
104    ///
105    /// This error occurs when parsing epub files, the library cannot
106    /// directly or indirectly identify the epub version number.
107    #[error(
108        "Unrecognized EPUB version: Unable to identify version number and version characteristics from epub file"
109    )]
110    UnrecognizedEpubVersion,
111
112    /// Unsupported encryption method error
113    ///
114    /// This error is triggered when attempting to decrypt a resource that uses
115    /// an encryption method not supported by this library.
116    ///
117    /// Currently, this library only supports:
118    /// - IDPF Font Obfuscation
119    /// - Adobe Font Obfuscation
120    #[error("Unsupported encryption method: The \"{method}\" encryption method is not supported.")]
121    UnsupportedEncryptedMethod { method: String },
122
123    /// Unusable compression method error
124    ///
125    /// This error occurs when an EPUB file uses an unsupported compression method.
126    #[error(
127        "Unusable compression method: The \"{file}\" file uses the unsupported \"{method}\" compression method."
128    )]
129    UnusableCompressionMethod { file: String, method: String },
130
131    /// UTF-8 decoding error
132    ///
133    /// This error occurs when attempting to decode byte data into a UTF-8 string
134    /// but the data is not formatted correctly.
135    #[error("Decode error: {source}")]
136    Utf8DecodeError { source: std::string::FromUtf8Error },
137
138    /// UTF-16 decoding error
139    ///
140    /// This error occurs when attempting to decode byte data into a UTF-16 string
141    /// but the data is not formatted correctly.
142    #[error("Decode error: {source}")]
143    Utf16DecodeError { source: std::string::FromUtf16Error },
144
145    /// QuickXml error
146    ///
147    /// This error occurs when parsing XML data using the QuickXml library.
148    #[error("QuickXml error: {source}")]
149    QuickXmlError { source: quick_xml::Error },
150}
151
152impl From<zip::result::ZipError> for EpubError {
153    fn from(value: zip::result::ZipError) -> Self {
154        EpubError::ArchiveError { source: value }
155    }
156}
157
158impl From<quick_xml::Error> for EpubError {
159    fn from(value: quick_xml::Error) -> Self {
160        EpubError::QuickXmlError { source: value }
161    }
162}
163
164impl From<std::io::Error> for EpubError {
165    fn from(value: std::io::Error) -> Self {
166        EpubError::IOError { source: value }
167    }
168}
169
170impl From<std::string::FromUtf8Error> for EpubError {
171    fn from(value: std::string::FromUtf8Error) -> Self {
172        EpubError::Utf8DecodeError { source: value }
173    }
174}
175
176impl From<std::string::FromUtf16Error> for EpubError {
177    fn from(value: std::string::FromUtf16Error) -> Self {
178        EpubError::Utf16DecodeError { source: value }
179    }
180}
181
182#[cfg(feature = "builder")]
183impl From<EpubBuilderError> for EpubError {
184    fn from(value: EpubBuilderError) -> Self {
185        EpubError::EpubBuilderError { source: value }
186    }
187}
188
189#[cfg(test)]
190impl PartialEq for EpubError {
191    fn eq(&self, other: &Self) -> bool {
192        match (self, other) {
193            (
194                Self::MissingRequiredAttribute {
195                    tag: l_tag,
196                    attribute: l_attribute,
197                },
198                Self::MissingRequiredAttribute {
199                    tag: r_tag,
200                    attribute: r_attribute,
201                },
202            ) => l_tag == r_tag && l_attribute == r_attribute,
203            (
204                Self::NonCanonicalEpub {
205                    expected_file: l_expected_file,
206                },
207                Self::NonCanonicalEpub {
208                    expected_file: r_expected_file,
209                },
210            ) => l_expected_file == r_expected_file,
211            (Self::NonCanonicalFile { tag: l_tag }, Self::NonCanonicalFile { tag: r_tag }) => {
212                l_tag == r_tag
213            }
214            (
215                Self::RealtiveLinkLeakage { path: l_path },
216                Self::RealtiveLinkLeakage { path: r_path },
217            ) => l_path == r_path,
218            (Self::ResourceIdNotExist { id: l_id }, Self::ResourceIdNotExist { id: r_id }) => {
219                l_id == r_id
220            }
221            (
222                Self::ResourceNotFound {
223                    resource: l_resource,
224                },
225                Self::ResourceNotFound {
226                    resource: r_resource,
227                },
228            ) => l_resource == r_resource,
229            (
230                Self::UnsupportedEncryptedMethod { method: l_method },
231                Self::UnsupportedEncryptedMethod { method: r_method },
232            ) => l_method == r_method,
233            (
234                Self::UnusableCompressionMethod {
235                    file: l_file,
236                    method: l_method,
237                },
238                Self::UnusableCompressionMethod {
239                    file: r_file,
240                    method: r_method,
241                },
242            ) => l_file == r_file && l_method == r_method,
243            (
244                Self::Utf8DecodeError { source: l_source },
245                Self::Utf8DecodeError { source: r_source },
246            ) => l_source == r_source,
247
248            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
249        }
250    }
251}
252
253/// Types of errors that can occur during EPUB build
254///
255/// This enumeration defines various error conditions that may occur
256/// when creating EPUB files using the `builder` function. These errors
257/// are typically related to EPUB specification requirements or validation
258/// rules during the build process.
259#[cfg(feature = "builder")]
260#[derive(Debug, Error)]
261pub enum EpubBuilderError {
262    /// Illegal manifest path error
263    ///
264    /// This error is triggered when the path corresponding to a resource ID
265    /// in the manifest begins with "../". Using relative paths starting with "../"
266    /// when building the manifest fails to determine the 'current location',
267    /// making it impossible to pinpoint the resource.
268    #[error(
269        "A manifest with id '{manifest_id}' should not use a relative path starting with '../'."
270    )]
271    IllegalManifestPath { manifest_id: String },
272
273    /// Manifest Circular Reference error
274    ///
275    /// This error is triggered when a fallback relationship between manifest items forms a cycle.
276    #[error("Circular reference detected in fallback chain for '{fallback_chain}'.")]
277    ManifestCircularReference { fallback_chain: String },
278
279    /// Manifest resource not found error
280    ///
281    /// This error is triggered when a manifest item specifies a fallback resource ID that does not exist.
282    #[error("Fallback resource '{manifest_id}' does not exist in manifest.")]
283    ManifestNotFound { manifest_id: String },
284
285    /// Missing necessary metadata error
286    ///
287    /// This error is triggered when the basic metadata required to build a valid EPUB is missing.
288    /// The following must be included: title, language, and an identifier with a 'pub-id' ID.
289    #[error("Requires at least one 'title', 'language', and 'identifier' with id 'pub-id'.")]
290    MissingNecessaryMetadata,
291
292    /// Navigation information uninitialized error
293    ///
294    /// This error is triggered when attempting to build an EPUB but without setting navigation information.
295    #[error("Navigation information is not set.")]
296    NavigationInfoUninitalized,
297
298    /// Missing rootfile error
299    ///
300    /// This error is triggered when attempting to build an EPUB without adding any 'rootfile'.
301    #[error("Need at least one rootfile.")]
302    MissingRootfile,
303
304    /// Target is not a file error
305    ///
306    /// This error is triggered when the specified target path is not a file.
307    #[error("Expect a file, but '{target_path}' is not a file.")]
308    TargetIsNotFile { target_path: String },
309
310    /// Too many nav flags error
311    ///
312    /// This error is triggered when the manifest contains multiple items with
313    /// the `nav` attribute. The EPUB specification requires that each EPUB have
314    /// **only one** navigation document.
315    #[error("There are too many items with 'nav' property in the manifest.")]
316    TooManyNavFlags,
317
318    /// Unknown file format error
319    ///
320    /// This error is triggered when the format type of the specified file cannot be analyzed.
321    #[error("Unable to analyze the file '{file_path}' type.")]
322    UnknowFileFormat { file_path: String },
323}