rust-apk 1.0.0

Build, align, sign, verify, and inspect Android APKs in pure Rust
Documentation
//! Error types.
//!
//! Every fallible operation returns a domain-specific error enum whose
//! variants carry the offending values. [`Error`] aggregates them, so `?`
//! works across domains while callers can still match precisely:
//!
//! ```
//! use apk::{Error, ValueError, PackageName, PackageNameError};
//!
//! let error = Error::from(PackageName::try_new("app").unwrap_err());
//! assert!(matches!(
//!     error,
//!     Error::Value(ValueError::PackageName(PackageNameError::TooFewSegments { segments: 1 }))
//! ));
//! ```

use std::fmt;
use std::io;
use std::path::PathBuf;

pub use crate::archive::ZipError;
pub use crate::build::BuildError;

pub use crate::manifest::ManifestError;
pub use crate::native::NativeError;
pub use crate::res::{
    ChunkError, LiteralError, LocaleError, QualifierError, ResourceError, ResourceNameError, ResourceRefError,
    StringPoolError, TableError,
};
pub use crate::sign::{CoverageError, JarError, KeyError, SignError, VerifyError};
pub use crate::types::{
    ApiLevelError, ArchivePathError, ClassNameError, LibraryNameError, PackageNameError, PermissionNameError,
    UnknownAbi, VersionCodeError,
};
pub use crate::xml::XmlError;

/// A `Result` defaulting to this crate's [`Error`].
pub type Result<T, E = Error> = std::result::Result<T, E>;

/// Any failure produced by this crate.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
    /// A file system operation on a named path failed.
    #[error("failed to {operation} `{}`", path.display())]
    File {
        operation: FileOperation,
        path: PathBuf,
        #[source]
        source: io::Error,
    },
    /// Reading or writing a caller-supplied stream failed.
    #[error(transparent)]
    Io(#[from] io::Error),
    #[error(transparent)]
    Value(#[from] ValueError),
    #[error(transparent)]
    Zip(#[from] ZipError),
    #[error(transparent)]
    Table(#[from] TableError),
    #[error(transparent)]
    Xml(#[from] XmlError),
    #[error(transparent)]
    Manifest(#[from] ManifestError),
    #[error(transparent)]
    Resource(#[from] ResourceError),
    #[error(transparent)]
    Native(#[from] NativeError),
    #[error(transparent)]
    Key(#[from] KeyError),
    #[error(transparent)]
    Sign(#[from] SignError),
    #[error(transparent)]
    Verify(#[from] VerifyError),
    #[error(transparent)]
    Build(#[from] BuildError),
}

/// The file system operation that failed in [`Error::File`].
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum FileOperation {
    Open,
    Read,
    Create,
    Write,
    Inspect,
    Persist,
}

impl fmt::Display for FileOperation {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(match self {
            Self::Open => "open",
            Self::Read => "read",
            Self::Create => "create",
            Self::Write => "write",
            Self::Inspect => "inspect",
            Self::Persist => "persist",
        })
    }
}

impl Error {
    /// Adapts an `io::Error` on `path` into [`Error::File`].
    pub(crate) fn file(operation: FileOperation, path: impl Into<PathBuf>) -> impl FnOnce(io::Error) -> Self {
        let path = path.into();
        move |source| Self::File { operation, path, source }
    }
}

/// A value rejected at a typed boundary.
#[derive(Clone, Debug, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum ValueError {
    #[error(transparent)]
    PackageName(#[from] PackageNameError),
    #[error(transparent)]
    ClassName(#[from] ClassNameError),
    #[error(transparent)]
    PermissionName(#[from] PermissionNameError),
    #[error(transparent)]
    LibraryName(#[from] LibraryNameError),
    #[error(transparent)]
    ArchivePath(#[from] ArchivePathError),
    #[error("invalid API level: {0}")]
    ApiLevel(#[from] ApiLevelError),
    #[error("invalid version code: {0}")]
    VersionCode(#[from] VersionCodeError),
    #[error(transparent)]
    Abi(#[from] UnknownAbi),
    #[error(transparent)]
    ResourceName(#[from] ResourceNameError),
    #[error(transparent)]
    ResourceRef(#[from] ResourceRefError),
    #[error(transparent)]
    Locale(#[from] LocaleError),
    #[error(transparent)]
    Qualifier(#[from] QualifierError),
    #[error(transparent)]
    Literal(#[from] LiteralError),
}

/// Lets `?` lift each validation error straight into [`Error`].
macro_rules! value_errors_into_error {
    ($($ty:ty),+ $(,)?) => {
        $(impl From<$ty> for Error {
            fn from(error: $ty) -> Self {
                Self::Value(error.into())
            }
        })+
    };
}

value_errors_into_error!(
    PackageNameError,
    ClassNameError,
    PermissionNameError,
    LibraryNameError,
    ArchivePathError,
    ApiLevelError,
    VersionCodeError,
    UnknownAbi,
    ResourceNameError,
    ResourceRefError,
    LocaleError,
    QualifierError,
    LiteralError,
);