Skip to main content

aion_package/
error.rs

1//! Error taxonomy for malformed `.aion` packages.
2
3/// Errors produced while validating or loading a `.aion` package.
4#[derive(thiserror::Error, Debug)]
5pub enum PackageError {
6    /// The archive could not be read as a ZIP container.
7    #[error("failed to read .aion ZIP archive: {0}")]
8    ArchiveRead(#[from] zip::result::ZipError),
9
10    /// The archive does not contain the required root manifest.
11    #[error("missing required manifest.json entry")]
12    MissingManifest,
13
14    /// A module uses a namespace owned by the engine's native NIF layer.
15    #[error(
16        "module `{module}` uses an engine-reserved namespace and must not ship as package bytecode"
17    )]
18    ReservedModuleName {
19        /// The offending logical module name.
20        module: String,
21    },
22
23    /// The archive could not be written as a ZIP container.
24    #[error("failed to write .aion ZIP archive: {0}")]
25    ArchiveWrite(zip::result::ZipError),
26
27    /// The archive target could not be written to the filesystem or memory buffer.
28    #[error("failed to write .aion archive bytes: {source}")]
29    ArchiveWriteIo {
30        /// I/O failure reported by the write target.
31        source: std::io::Error,
32    },
33
34    /// The manifest entry is present but is not valid manifest JSON.
35    #[error("failed to parse manifest.json: {source}")]
36    ManifestParse {
37        /// JSON parsing failure reported by `serde_json`.
38        source: serde_json::Error,
39    },
40
41    /// The manifest could not be serialised for writing into the archive.
42    #[error("failed to serialise manifest.json: {source}")]
43    ManifestSerialise {
44        /// JSON serialisation failure reported by `serde_json`.
45        source: serde_json::Error,
46    },
47
48    /// The manifest declares a format version this crate does not support.
49    #[error("unknown .aion format_version {found}")]
50    UnknownFormatVersion {
51        /// Unsupported format version found in the manifest.
52        found: u32,
53    },
54
55    /// The manifest entry module is not present in the beam set.
56    #[error("missing entry module `{module}` in beam set")]
57    MissingEntryModule {
58        /// Logical entry module named by the manifest.
59        module: String,
60    },
61
62    /// The manifest version does not match the hash recomputed from beams.
63    #[error("package integrity mismatch: expected version `{expected}`, computed `{computed}`")]
64    IntegrityMismatch {
65        /// Version claimed by the manifest.
66        expected: String,
67        /// Version recomputed from package beams.
68        computed: String,
69    },
70
71    /// A beam archive entry is malformed or ambiguous.
72    #[error("malformed beam entry `{entry}`")]
73    MalformedBeamEntry {
74        /// Archive entry or logical module name that failed validation.
75        entry: String,
76    },
77
78    /// The archive's entries inflate past the caller's extraction budget.
79    #[error(
80        "archive contents inflate past the extraction limit of {limit} bytes; refusing to extract further"
81    )]
82    InflatedSizeExceeded {
83        /// The caller-configured inflate ceiling in bytes.
84        limit: u64,
85    },
86
87    /// Explicit-timeout package identity was requested for a manifest that
88    /// declares no workflow timeout, so there is no value to bind into the hash.
89    #[error(
90        "explicit-timeout package identity requires an authored workflow timeout, but the manifest declares none"
91    )]
92    ExplicitTimeoutWithoutValue,
93}
94
95#[cfg(test)]
96mod tests {
97    use super::PackageError;
98
99    fn assert_send_sync<T: Send + Sync>() {}
100
101    #[test]
102    fn package_error_is_send_and_sync() {
103        assert_send_sync::<PackageError>();
104    }
105
106    #[test]
107    fn display_messages_name_the_failed_condition() {
108        assert_eq!(
109            PackageError::MissingManifest.to_string(),
110            "missing required manifest.json entry"
111        );
112        assert_eq!(
113            PackageError::ArchiveWriteIo {
114                source: std::io::Error::other("disk full"),
115            }
116            .to_string(),
117            "failed to write .aion archive bytes: disk full"
118        );
119        assert_eq!(
120            PackageError::UnknownFormatVersion { found: 99 }.to_string(),
121            "unknown .aion format_version 99"
122        );
123        assert_eq!(
124            PackageError::MissingEntryModule {
125                module: "workflow/main".to_owned(),
126            }
127            .to_string(),
128            "missing entry module `workflow/main` in beam set"
129        );
130        assert_eq!(
131            PackageError::IntegrityMismatch {
132                expected: "expected".to_owned(),
133                computed: "computed".to_owned(),
134            }
135            .to_string(),
136            "package integrity mismatch: expected version `expected`, computed `computed`"
137        );
138        assert_eq!(
139            PackageError::MalformedBeamEntry {
140                entry: "beam/workflow.beam".to_owned(),
141            }
142            .to_string(),
143            "malformed beam entry `beam/workflow.beam`"
144        );
145        assert_eq!(
146            PackageError::InflatedSizeExceeded { limit: 1024 }.to_string(),
147            "archive contents inflate past the extraction limit of 1024 bytes; refusing to extract further"
148        );
149    }
150}