use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum NameError {
Empty,
Relative,
Separator(char),
Colon,
ReservedForMetadata,
NotUtf8,
}
impl fmt::Display for NameError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Empty => f.write_str("payload.file is empty (SPEC 2.3)"),
Self::Relative => f.write_str("payload.file is `.` or `..` (SPEC 2.3)"),
Self::Separator(c) => write!(f, "payload.file contains {c:?}, so it is a path rather than a filename (SPEC 2.3)"),
Self::Colon => f.write_str("payload.file contains ':', which is read as rooting a path on some platforms (SPEC 2.3)"),
Self::ReservedForMetadata => write!(f, "payload.file is {:?}, which names the metadata member (SPEC 2.3)", crate::METADATA_MEMBER),
Self::NotUtf8 => f.write_str("the name is not UTF-8, and payload.file is a TOML string (SPEC 2.2)"),
}
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum Malformed {
NotAnArchive(String),
NoMetadataMember,
MetadataNotUtf8,
MetadataNotToml(String),
MissingKey(&'static str),
KeyNotAString(&'static str),
PayloadName(NameError),
NoPayloadMember(String),
PayloadIsSymlink(String),
PayloadPathName {
path: std::path::PathBuf,
cause: NameError,
},
Disagrees {
key: &'static str,
found: String,
writing: String,
},
PayloadNotATable,
}
impl fmt::Display for Malformed {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NotAnArchive(why) => write!(f, "not a readable ZIP archive: {why}"),
Self::NoMetadataMember => {
write!(f, "no member named {:?} (SPEC 2.1)", crate::METADATA_MEMBER)
}
Self::MetadataNotUtf8 => {
write!(f, "{:?} is not UTF-8 (SPEC 2.2)", crate::METADATA_MEMBER)
}
Self::MetadataNotToml(why) => write!(
f,
"{:?} is not a valid TOML document (SPEC 2.2): {why}",
crate::METADATA_MEMBER
),
Self::MissingKey(k) => write!(f, "the metadata has no `{k}` key (SPEC 2.2)"),
Self::KeyNotAString(k) => write!(f, "the metadata's `{k}` is not a string (SPEC 2.2)"),
Self::PayloadName(e) => e.fmt(f),
Self::NoPayloadMember(n) => write!(
f,
"payload.file names {n:?}, which the archive does not contain (SPEC 2.1)"
),
Self::PayloadIsSymlink(n) => write!(
f,
"the payload member {n:?} is a symbolic link entry (SPEC 2.3)"
),
Self::PayloadPathName { path, cause } => write!(
f,
"{} cannot be packed under its own name: {cause}",
path.display()
),
Self::Disagrees { key, found, writing } => write!(
f,
"the metadata sets `{key}` to {found:?}, but this container is being written with {writing:?} (SPEC 2.2)"
),
Self::PayloadNotATable => f.write_str("the metadata's `payload` is not a table (SPEC 2.2)"),
}
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum Unsupported {
Version(String),
Compression(u16),
Encrypted,
Archive(String),
}
impl fmt::Display for Unsupported {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Version(v) => write!(
f,
"slipcase_version {v:?} is not one this build implements (SPEC 3)"
),
Self::Compression(m) => write!(
f,
"compression method {m} is not compiled into this build (SPEC 2.5)"
),
Self::Encrypted => f.write_str("the member is encrypted (SPEC 2.5)"),
Self::Archive(why) => write!(f, "this build cannot read the archive: {why}"),
}
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
Io(std::io::Error),
Malformed(Malformed),
Unsupported(Unsupported),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(e) => write!(f, "i/o error: {e}"),
Self::Malformed(e) => e.fmt(f),
Self::Unsupported(e) => e.fmt(f),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io(e) => Some(e),
Self::Malformed(_) | Self::Unsupported(_) => None,
}
}
}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Self::Io(e)
}
}
impl From<Malformed> for Error {
fn from(e: Malformed) -> Self {
Self::Malformed(e)
}
}
impl From<NameError> for Error {
fn from(e: NameError) -> Self {
Self::Malformed(Malformed::PayloadName(e))
}
}
impl From<Unsupported> for Error {
fn from(e: Unsupported) -> Self {
Self::Unsupported(e)
}
}
impl From<zip::result::ZipError> for Error {
fn from(e: zip::result::ZipError) -> Self {
use zip::result::ZipError as Z;
match e {
Z::Io(e) => Self::Io(e),
Z::InvalidArchive(why) => Self::Malformed(Malformed::NotAnArchive(why.to_string())),
Z::UnsupportedArchive(why) if why == Z::PASSWORD_REQUIRED => {
Self::Unsupported(Unsupported::Encrypted)
}
Z::UnsupportedArchive(why) => Self::Unsupported(Unsupported::Archive(why.to_string())),
Z::CompressionMethodNotSupported(m) => Self::Unsupported(Unsupported::Compression(m)),
Z::InvalidPassword => Self::Unsupported(Unsupported::Encrypted),
Z::FileNotFound => Self::Malformed(Malformed::NotAnArchive(
"a member named in the central directory is not there".into(),
)),
other => Self::Malformed(Malformed::NotAnArchive(other.to_string())),
}
}
}
pub type Result<T> = std::result::Result<T, Error>;