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