Skip to main content

apk/
error.rs

1//! Error types.
2//!
3//! Every fallible operation returns a domain-specific error enum whose
4//! variants carry the offending values. [`Error`] aggregates them, so `?`
5//! works across domains while callers can still match precisely:
6//!
7//! ```
8//! use apk::{Error, ValueError, PackageName, PackageNameError};
9//!
10//! let error = Error::from(PackageName::try_new("app").unwrap_err());
11//! assert!(matches!(
12//!     error,
13//!     Error::Value(ValueError::PackageName(PackageNameError::TooFewSegments { segments: 1 }))
14//! ));
15//! ```
16
17use std::fmt;
18use std::io;
19use std::path::PathBuf;
20
21pub use crate::archive::ZipError;
22pub use crate::build::BuildError;
23
24pub use crate::manifest::ManifestError;
25pub use crate::native::NativeError;
26pub use crate::res::{
27    ChunkError, LiteralError, LocaleError, QualifierError, ResourceError, ResourceNameError, ResourceRefError,
28    StringPoolError, TableError,
29};
30pub use crate::sign::{CoverageError, JarError, KeyError, SignError, VerifyError};
31pub use crate::types::{
32    ApiLevelError, ArchivePathError, ClassNameError, LibraryNameError, PackageNameError, PermissionNameError,
33    UnknownAbi, VersionCodeError,
34};
35pub use crate::xml::XmlError;
36
37/// A `Result` defaulting to this crate's [`Error`].
38pub type Result<T, E = Error> = std::result::Result<T, E>;
39
40/// Any failure produced by this crate.
41#[derive(Debug, thiserror::Error)]
42#[non_exhaustive]
43pub enum Error {
44    /// A file system operation on a named path failed.
45    #[error("failed to {operation} `{}`", path.display())]
46    File {
47        operation: FileOperation,
48        path: PathBuf,
49        #[source]
50        source: io::Error,
51    },
52    /// Reading or writing a caller-supplied stream failed.
53    #[error(transparent)]
54    Io(#[from] io::Error),
55    #[error(transparent)]
56    Value(#[from] ValueError),
57    #[error(transparent)]
58    Zip(#[from] ZipError),
59    #[error(transparent)]
60    Table(#[from] TableError),
61    #[error(transparent)]
62    Xml(#[from] XmlError),
63    #[error(transparent)]
64    Manifest(#[from] ManifestError),
65    #[error(transparent)]
66    Resource(#[from] ResourceError),
67    #[error(transparent)]
68    Native(#[from] NativeError),
69    #[error(transparent)]
70    Key(#[from] KeyError),
71    #[error(transparent)]
72    Sign(#[from] SignError),
73    #[error(transparent)]
74    Verify(#[from] VerifyError),
75    #[error(transparent)]
76    Build(#[from] BuildError),
77}
78
79/// The file system operation that failed in [`Error::File`].
80#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
81pub enum FileOperation {
82    Open,
83    Read,
84    Create,
85    Write,
86    Inspect,
87    Persist,
88}
89
90impl fmt::Display for FileOperation {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        f.write_str(match self {
93            Self::Open => "open",
94            Self::Read => "read",
95            Self::Create => "create",
96            Self::Write => "write",
97            Self::Inspect => "inspect",
98            Self::Persist => "persist",
99        })
100    }
101}
102
103impl Error {
104    /// Adapts an `io::Error` on `path` into [`Error::File`].
105    pub(crate) fn file(operation: FileOperation, path: impl Into<PathBuf>) -> impl FnOnce(io::Error) -> Self {
106        let path = path.into();
107        move |source| Self::File { operation, path, source }
108    }
109}
110
111/// A value rejected at a typed boundary.
112#[derive(Clone, Debug, PartialEq, thiserror::Error)]
113#[non_exhaustive]
114pub enum ValueError {
115    #[error(transparent)]
116    PackageName(#[from] PackageNameError),
117    #[error(transparent)]
118    ClassName(#[from] ClassNameError),
119    #[error(transparent)]
120    PermissionName(#[from] PermissionNameError),
121    #[error(transparent)]
122    LibraryName(#[from] LibraryNameError),
123    #[error(transparent)]
124    ArchivePath(#[from] ArchivePathError),
125    #[error("invalid API level: {0}")]
126    ApiLevel(#[from] ApiLevelError),
127    #[error("invalid version code: {0}")]
128    VersionCode(#[from] VersionCodeError),
129    #[error(transparent)]
130    Abi(#[from] UnknownAbi),
131    #[error(transparent)]
132    ResourceName(#[from] ResourceNameError),
133    #[error(transparent)]
134    ResourceRef(#[from] ResourceRefError),
135    #[error(transparent)]
136    Locale(#[from] LocaleError),
137    #[error(transparent)]
138    Qualifier(#[from] QualifierError),
139    #[error(transparent)]
140    Literal(#[from] LiteralError),
141}
142
143/// Lets `?` lift each validation error straight into [`Error`].
144macro_rules! value_errors_into_error {
145    ($($ty:ty),+ $(,)?) => {
146        $(impl From<$ty> for Error {
147            fn from(error: $ty) -> Self {
148                Self::Value(error.into())
149            }
150        })+
151    };
152}
153
154value_errors_into_error!(
155    PackageNameError,
156    ClassNameError,
157    PermissionNameError,
158    LibraryNameError,
159    ArchivePathError,
160    ApiLevelError,
161    VersionCodeError,
162    UnknownAbi,
163    ResourceNameError,
164    ResourceRefError,
165    LocaleError,
166    QualifierError,
167    LiteralError,
168);