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 /// Mutex error
61 ///
62 /// This error occurs when a mutex is poisoned, which means
63 /// that a thread has panicked while holding a lock on the mutex.
64 #[error("Mutex error: Mutex was poisoned.")]
65 MutexError,
66
67 /// Non-canonical EPUB structure error
68 ///
69 /// This error occurs when an EPUB file lacks some files or directory
70 /// structure that is required in EPUB specification.
71 #[error("Non-canonical epub: The \"{expected_file}\" file was not found.")]
72 NonCanonicalEpub { expected_file: String },
73
74 /// Non-canonical file structure error
75 ///
76 /// This error is triggered when the required XML elements in the
77 /// specification are missing from the EPUB file.
78 #[error("Non-canonical file: The \"{tag}\" elements was not found.")]
79 NonCanonicalFile { tag: String },
80
81 /// Missing supported file format error
82 ///
83 /// This error occurs when trying to get a resource but there isn't any supported file format.
84 /// It usually happens when there are no supported formats available in the fallback chain.
85 #[error(
86 "No supported file format: The fallback resource does not contain the file format you support."
87 )]
88 NoSupportedFileFormat,
89
90 /// Relative link leak error
91 ///
92 /// This error occurs when a relative path link is outside the scope
93 /// of an EPUB container, which is a security protection mechanism.
94 #[error("Relative link leakage: Path \"{path}\" is out of container range.")]
95 RealtiveLinkLeakage { path: String },
96
97 /// Unable to find the resource id error
98 ///
99 /// This error occurs when trying to get a resource by id but that id doesn't exist in the manifest.
100 #[error("Resource Id Not Exist: There is no resource item with id \"{id}\".")]
101 ResourceIdNotExist { id: String },
102
103 /// Unable to find the resource error
104 ///
105 /// This error occurs when an attempt is made to get a resource
106 /// but it does not exist in the EPUB container.
107 #[error("Resource not found: Unable to find resource from \"{resource}\".")]
108 ResourceNotFound { resource: String },
109
110 /// Unrecognized EPUB version error
111 ///
112 /// This error occurs when parsing epub files, the library cannot
113 /// directly or indirectly identify the epub version number.
114 #[error(
115 "Unrecognized EPUB version: Unable to identify version number and version characteristics from epub file"
116 )]
117 UnrecognizedEpubVersion,
118
119 /// Unsupported encryption method error
120 ///
121 /// This error is triggered when attempting to decrypt a resource that uses
122 /// an encryption method not supported by this library.
123 ///
124 /// Currently, this library only supports:
125 /// - IDPF Font Obfuscation
126 /// - Adobe Font Obfuscation
127 #[error("Unsupported encryption method: The \"{method}\" encryption method is not supported.")]
128 UnsupportedEncryptedMethod { method: String },
129
130 /// Unusable compression method error
131 ///
132 /// This error occurs when an EPUB file uses an unsupported compression method.
133 #[error(
134 "Unusable compression method: The \"{file}\" file uses the unsupported \"{method}\" compression method."
135 )]
136 UnusableCompressionMethod { file: String, method: String },
137
138 /// UTF-8 decoding error
139 ///
140 /// This error occurs when attempting to decode byte data into a UTF-8 string
141 /// but the data is not formatted correctly.
142 #[error("Decode error: {source}")]
143 Utf8DecodeError { source: std::string::FromUtf8Error },
144
145 /// UTF-16 decoding error
146 ///
147 /// This error occurs when attempting to decode byte data into a UTF-16 string
148 /// but the data is not formatted correctly.
149 #[error("Decode error: {source}")]
150 Utf16DecodeError { source: std::string::FromUtf16Error },
151
152 /// WalkDir error
153 ///
154 /// This error occurs when using the WalkDir library to traverse the directory.
155 #[cfg(feature = "builder")]
156 #[error("WalkDir error: {source}")]
157 WalkDirError { source: walkdir::Error },
158
159 /// QuickXml error
160 ///
161 /// This error occurs when parsing XML data using the QuickXml library.
162 #[error("QuickXml error: {source}")]
163 QuickXmlError { source: quick_xml::Error },
164}
165
166impl From<zip::result::ZipError> for EpubError {
167 fn from(value: zip::result::ZipError) -> Self {
168 EpubError::ArchiveError { source: value }
169 }
170}
171
172impl From<quick_xml::Error> for EpubError {
173 fn from(value: quick_xml::Error) -> Self {
174 EpubError::QuickXmlError { source: value }
175 }
176}
177
178impl From<std::io::Error> for EpubError {
179 fn from(value: std::io::Error) -> Self {
180 EpubError::IOError { source: value }
181 }
182}
183
184impl From<std::string::FromUtf8Error> for EpubError {
185 fn from(value: std::string::FromUtf8Error) -> Self {
186 EpubError::Utf8DecodeError { source: value }
187 }
188}
189
190impl From<std::string::FromUtf16Error> for EpubError {
191 fn from(value: std::string::FromUtf16Error) -> Self {
192 EpubError::Utf16DecodeError { source: value }
193 }
194}
195
196impl<T> From<std::sync::PoisonError<T>> for EpubError {
197 fn from(_value: std::sync::PoisonError<T>) -> Self {
198 EpubError::MutexError
199 }
200}
201
202#[cfg(feature = "builder")]
203impl From<EpubBuilderError> for EpubError {
204 fn from(value: EpubBuilderError) -> Self {
205 EpubError::EpubBuilderError { source: value }
206 }
207}
208
209#[cfg(feature = "builder")]
210impl From<walkdir::Error> for EpubError {
211 fn from(value: walkdir::Error) -> Self {
212 EpubError::WalkDirError { source: value }
213 }
214}
215
216#[cfg(test)]
217impl PartialEq for EpubError {
218 fn eq(&self, other: &Self) -> bool {
219 match (self, other) {
220 (
221 Self::MissingRequiredAttribute {
222 tag: l_tag,
223 attribute: l_attribute,
224 },
225 Self::MissingRequiredAttribute {
226 tag: r_tag,
227 attribute: r_attribute,
228 },
229 ) => l_tag == r_tag && l_attribute == r_attribute,
230 (
231 Self::NonCanonicalEpub {
232 expected_file: l_expected_file,
233 },
234 Self::NonCanonicalEpub {
235 expected_file: r_expected_file,
236 },
237 ) => l_expected_file == r_expected_file,
238 (Self::NonCanonicalFile { tag: l_tag }, Self::NonCanonicalFile { tag: r_tag }) => {
239 l_tag == r_tag
240 }
241 (
242 Self::RealtiveLinkLeakage { path: l_path },
243 Self::RealtiveLinkLeakage { path: r_path },
244 ) => l_path == r_path,
245 (Self::ResourceIdNotExist { id: l_id }, Self::ResourceIdNotExist { id: r_id }) => {
246 l_id == r_id
247 }
248 (
249 Self::ResourceNotFound {
250 resource: l_resource,
251 },
252 Self::ResourceNotFound {
253 resource: r_resource,
254 },
255 ) => l_resource == r_resource,
256 (
257 Self::UnsupportedEncryptedMethod { method: l_method },
258 Self::UnsupportedEncryptedMethod { method: r_method },
259 ) => l_method == r_method,
260 (
261 Self::UnusableCompressionMethod {
262 file: l_file,
263 method: l_method,
264 },
265 Self::UnusableCompressionMethod {
266 file: r_file,
267 method: r_method,
268 },
269 ) => l_file == r_file && l_method == r_method,
270 (
271 Self::Utf8DecodeError { source: l_source },
272 Self::Utf8DecodeError { source: r_source },
273 ) => l_source == r_source,
274
275 (
276 Self::EpubBuilderError { source: l_source },
277 Self::EpubBuilderError { source: r_source },
278 ) => l_source == r_source,
279
280 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
281 }
282 }
283}
284
285/// Types of errors that can occur during EPUB build
286///
287/// This enumeration defines various error conditions that may occur
288/// when creating EPUB files using the `builder` function. These errors
289/// are typically related to EPUB specification requirements or validation
290/// rules during the build process.
291#[cfg(feature = "builder")]
292#[derive(Debug, Error)]
293#[cfg_attr(test, derive(PartialEq))]
294pub enum EpubBuilderError {
295 /// Illegal manifest path error
296 ///
297 /// This error is triggered when the path corresponding to a resource ID
298 /// in the manifest begins with "../". Using relative paths starting with "../"
299 /// when building the manifest fails to determine the 'current location',
300 /// making it impossible to pinpoint the resource.
301 #[error(
302 "A manifest with id '{manifest_id}' should not use a relative path starting with '../'."
303 )]
304 IllegalManifestPath { manifest_id: String },
305
306 /// Invalid rootfile path error
307 ///
308 /// This error is triggered when the rootfile path in the container.xml is invalid.
309 /// According to the EPUB specification, rootfile paths must be relative paths
310 /// that do not start with "../" to prevent directory traversal outside the EPUB container.
311 #[error("A rootfile path should be a relative path and not start with '../'.")]
312 IllegalRootfilePath,
313
314 /// Manifest Circular Reference error
315 ///
316 /// This error is triggered when a fallback relationship between manifest items forms a cycle.
317 #[error("Circular reference detected in fallback chain for '{fallback_chain}'.")]
318 ManifestCircularReference { fallback_chain: String },
319
320 /// Manifest resource not found error
321 ///
322 /// This error is triggered when a manifest item specifies a fallback resource ID that does not exist.
323 #[error("Fallback resource '{manifest_id}' does not exist in manifest.")]
324 ManifestNotFound { manifest_id: String },
325
326 /// Missing necessary metadata error
327 ///
328 /// This error is triggered when the basic metadata required to build a valid EPUB is missing.
329 /// The following must be included: title, language, and an identifier with a 'pub-id' ID.
330 #[error("Requires at least one 'title', 'language', and 'identifier' with id 'pub-id'.")]
331 MissingNecessaryMetadata,
332
333 /// Navigation information uninitialized error
334 ///
335 /// This error is triggered when attempting to build an EPUB but without setting navigation information.
336 #[error("Navigation information is not set.")]
337 NavigationInfoUninitalized,
338
339 /// Missing rootfile error
340 ///
341 /// This error is triggered when attempting to build an EPUB without adding any 'rootfile'.
342 #[error("Need at least one rootfile.")]
343 MissingRootfile,
344
345 /// Target is not a file error
346 ///
347 /// This error is triggered when the specified target path is not a file.
348 #[error("Expect a file, but '{target_path}' is not a file.")]
349 TargetIsNotFile { target_path: String },
350
351 /// Too many nav flags error
352 ///
353 /// This error is triggered when the manifest contains multiple items with
354 /// the `nav` attribute. The EPUB specification requires that each EPUB have
355 /// **only one** navigation document.
356 #[error("There are too many items with 'nav' property in the manifest.")]
357 TooManyNavFlags,
358
359 /// Unknown file format error
360 ///
361 /// This error is triggered when the format type of the specified file cannot be analyzed.
362 #[error("Unable to analyze the file '{file_path}' type.")]
363 UnknownFileFormat { file_path: String },
364}