use thiserror::Error;
#[derive(Debug, Error)]
pub enum EpubError {
#[error("Archive error: {source}")]
ArchiveError { source: zip::result::ZipError },
#[error("Decode error: The data is empty.")]
EmptyDataError,
#[cfg(feature = "builder")]
#[error("Epub builder error: {source}")]
EpubBuilderError { source: EpubBuilderError },
#[error(
"Failed parsing XML error: Unknown problems occurred during XML parsing, causing parsing failure."
)]
FailedParsingXml,
#[error("IO error: {source}")]
IOError { source: std::io::Error },
#[error(
"Missing required attribute: The \"{attribute}\" attribute is a must attribute for the \"{tag}\" element."
)]
MissingRequiredAttribute { tag: String, attribute: String },
#[error("Mutex error: Mutex was poisoned.")]
MutexError,
#[error("Non-canonical epub: The \"{expected_file}\" file was not found.")]
NonCanonicalEpub { expected_file: String },
#[error("Non-canonical file: The \"{tag}\" elements was not found.")]
NonCanonicalFile { tag: String },
#[error(
"No supported file format: The fallback resource does not contain the file format you support."
)]
NoSupportedFileFormat,
#[error("Relative link leakage: Path \"{path}\" is out of container range.")]
RelativeLinkLeakage { path: String },
#[error("Resource Id Not Exist: There is no resource item with id \"{id}\".")]
ResourceIdNotExist { id: String },
#[error("Resource not found: Unable to find resource from \"{resource}\".")]
ResourceNotFound { resource: String },
#[error(
"Unrecognized EPUB version: Unable to identify version number and version characteristics from epub file"
)]
UnrecognizedEpubVersion,
#[error("Unsupported encryption method: The \"{method}\" encryption method is not supported.")]
UnsupportedEncryptedMethod { method: String },
#[error(
"Unusable compression method: The \"{file}\" file uses the unsupported \"{method}\" compression method."
)]
UnusableCompressionMethod { file: String, method: String },
#[error("Decode error: {source}")]
Utf8DecodeError { source: std::string::FromUtf8Error },
#[error("Decode error: {source}")]
Utf16DecodeError { source: std::string::FromUtf16Error },
#[cfg(feature = "builder")]
#[error("WalkDir error: {source}")]
WalkDirError { source: walkdir::Error },
#[error("QuickXml error: {source}")]
QuickXmlError { source: quick_xml::Error },
}
impl From<zip::result::ZipError> for EpubError {
fn from(value: zip::result::ZipError) -> Self {
EpubError::ArchiveError { source: value }
}
}
impl From<quick_xml::Error> for EpubError {
fn from(value: quick_xml::Error) -> Self {
EpubError::QuickXmlError { source: value }
}
}
impl From<std::io::Error> for EpubError {
fn from(value: std::io::Error) -> Self {
EpubError::IOError { source: value }
}
}
impl From<std::string::FromUtf8Error> for EpubError {
fn from(value: std::string::FromUtf8Error) -> Self {
EpubError::Utf8DecodeError { source: value }
}
}
impl From<std::string::FromUtf16Error> for EpubError {
fn from(value: std::string::FromUtf16Error) -> Self {
EpubError::Utf16DecodeError { source: value }
}
}
impl<T> From<std::sync::PoisonError<T>> for EpubError {
fn from(_value: std::sync::PoisonError<T>) -> Self {
EpubError::MutexError
}
}
#[cfg(feature = "builder")]
impl From<EpubBuilderError> for EpubError {
fn from(value: EpubBuilderError) -> Self {
EpubError::EpubBuilderError { source: value }
}
}
#[cfg(feature = "builder")]
impl From<walkdir::Error> for EpubError {
fn from(value: walkdir::Error) -> Self {
EpubError::WalkDirError { source: value }
}
}
#[cfg(test)]
impl PartialEq for EpubError {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(
Self::MissingRequiredAttribute { tag: l_tag, attribute: l_attribute },
Self::MissingRequiredAttribute { tag: r_tag, attribute: r_attribute },
) => l_tag == r_tag && l_attribute == r_attribute,
(
Self::NonCanonicalEpub { expected_file: l_expected_file },
Self::NonCanonicalEpub { expected_file: r_expected_file },
) => l_expected_file == r_expected_file,
(Self::NonCanonicalFile { tag: l_tag }, Self::NonCanonicalFile { tag: r_tag }) => {
l_tag == r_tag
}
(
Self::RelativeLinkLeakage { path: l_path },
Self::RelativeLinkLeakage { path: r_path },
) => l_path == r_path,
(Self::ResourceIdNotExist { id: l_id }, Self::ResourceIdNotExist { id: r_id }) => {
l_id == r_id
}
(
Self::ResourceNotFound { resource: l_resource },
Self::ResourceNotFound { resource: r_resource },
) => l_resource == r_resource,
(
Self::UnsupportedEncryptedMethod { method: l_method },
Self::UnsupportedEncryptedMethod { method: r_method },
) => l_method == r_method,
(
Self::UnusableCompressionMethod { file: l_file, method: l_method },
Self::UnusableCompressionMethod { file: r_file, method: r_method },
) => l_file == r_file && l_method == r_method,
(
Self::Utf8DecodeError { source: l_source },
Self::Utf8DecodeError { source: r_source },
) => l_source == r_source,
#[cfg(feature = "builder")]
(
Self::EpubBuilderError { source: l_source },
Self::EpubBuilderError { source: r_source },
) => l_source == r_source,
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
}
}
}
#[cfg(feature = "builder")]
#[derive(Debug, Error)]
#[cfg_attr(test, derive(PartialEq))]
pub enum EpubBuilderError {
#[error(
"A manifest with id '{manifest_id}' should not use a relative path starting with '../'."
)]
IllegalManifestPath { manifest_id: String },
#[error("A rootfile path should be a relative path and not start with '../'.")]
IllegalRootfilePath,
#[error("The footnote locate must be in the range of [0, {max_locate}].")]
InvalidFootnoteLocate { max_locate: usize },
#[error("{error}")]
InvalidMathMLFormat { error: String },
#[error("The '{target_path}' target path is invalid.")]
InvalidTargetPath { target_path: String },
#[error("Circular reference detected in fallback chain for '{fallback_chain}'.")]
ManifestCircularReference { fallback_chain: String },
#[error("Fallback resource '{manifest_id}' does not exist in manifest.")]
ManifestNotFound { manifest_id: String },
#[error("Requires at least one 'title', 'language', and 'identifier' with id 'pub-id'.")]
MissingNecessaryMetadata,
#[error("The block '{block_type}' is missing necessary data '{missing_data}'")]
MissingNecessaryBlockData {
block_type: String,
missing_data: String,
},
#[error("Navigation information is not set.")]
NavigationInfoUninitalized,
#[error("The file format is not current block expected.")]
NotExpectedFileFormat,
#[error("Need at least one rootfile.")]
MissingRootfile,
#[error("Spine item '{idref}' references a manifest item that does not exist.")]
SpineManifestNotFound { idref: String },
#[error("Expect a file, but '{target_path}' is not a file.")]
TargetIsNotFile { target_path: String },
#[error("There are too many items with 'nav' property in the manifest.")]
TooManyNavFlags,
#[error("Unable to analyze the file '{file_path}' type.")]
UnknownFileFormat { file_path: String },
}