mf2_catalog/error.rs
1//! Error types.
2//!
3//! [`CatalogError`] is on the client path: a plain `#[repr(u8)]` enum that
4//! the reader returns and never formats. Its `Display` impl (thiserror)
5//! exists for servers and tools and is unreachable from the reader (B12,
6//! checked in CI). The build-side errors live behind the features that
7//! produce them.
8
9use thiserror::Error;
10
11/// Why [`crate::Catalog::new`] rejected a buffer (F4, F6, F9).
12#[derive(Clone, Copy, PartialEq, Eq, Debug, Error)]
13#[repr(u8)]
14#[non_exhaustive]
15pub enum CatalogError {
16 /// Not an `.mf2b` file: the magic bytes are missing or wrong.
17 #[error("not an .mf2b catalog")]
18 Magic,
19 /// A major format version this reader does not know (F9).
20 #[error("unsupported .mf2b format version")]
21 Version,
22 /// Compiled against another manifest: the wasm and the catalog disagree
23 /// on ids, slots, markup or functions (F6). Reload.
24 #[error("catalog was compiled against a different manifest")]
25 ManifestMismatch,
26 /// The buffer ends inside the header or the section table.
27 #[error("catalog is truncated")]
28 Truncated,
29 /// A header field is invalid: message count, direction or the locale tag.
30 #[error("invalid catalog header")]
31 Header,
32 /// Sections out of bounds, out of order or overlapping, a known kind
33 /// twice, or STRINGS not last.
34 #[error("invalid section table")]
35 SectionTable,
36 /// A required section is missing.
37 #[error("a required section is missing")]
38 MissingSection,
39 /// INDEX has the wrong size or an entry out of bounds or out of order.
40 #[error("invalid INDEX section")]
41 Index,
42 /// The LOCALE container or one of its plural entries is malformed.
43 #[error("invalid LOCALE section")]
44 Locale,
45 /// FUNCS is malformed.
46 #[error("invalid FUNCS section")]
47 Funcs,
48 /// FALLBACK is malformed.
49 #[error("invalid FALLBACK section")]
50 Fallback,
51 /// NAMES is malformed.
52 #[error("invalid NAMES section")]
53 Names,
54 /// IDS is malformed.
55 #[error("invalid IDS section")]
56 Ids,
57 /// STRINGS does not end with a NUL byte.
58 #[error("invalid STRINGS section")]
59 Strings,
60}
61
62/// Why the writer refused a catalog (`plans/02-catalog-format.md` §2.9).
63#[cfg(feature = "writer")]
64#[derive(Clone, PartialEq, Eq, Debug, Error)]
65#[non_exhaustive]
66pub enum WriteError {
67 /// A string contains U+0000, which has no syntax form and no catalog form.
68 #[error("string contains U+0000: {0:?}")]
69 Nul(alloc::string::String),
70 /// A variable is neither a local in scope nor in the message's slot list.
71 #[error("message {message}: variable ${name} is not in its slot list")]
72 UnknownVariable {
73 /// The message's index (its `MsgId` index).
74 message: usize,
75 /// The variable's name as written.
76 name: alloc::string::String,
77 },
78 /// A function is not in the manifest's function set.
79 #[error("message {message}: function :{name} is not in the manifest")]
80 UnknownFunction {
81 /// The message's index.
82 message: usize,
83 /// The function's identifier as written.
84 name: alloc::string::String,
85 },
86 /// An `.input` declaration whose name differs from its variable's.
87 #[error("message {message}: .input declares {name:?} for variable {variable:?}")]
88 InputName {
89 /// The message's index.
90 message: usize,
91 /// The declaration's name.
92 name: alloc::string::String,
93 /// Its variable's name.
94 variable: alloc::string::String,
95 },
96 /// A data-model node this format version cannot represent.
97 #[error("message {message}: unsupported data-model node")]
98 Unsupported {
99 /// The message's index.
100 message: usize,
101 },
102 /// The manifest and the messages do not fit together, or the manifest's
103 /// lists are not strictly ascending.
104 #[error("manifest: {0}")]
105 Manifest(&'static str),
106 /// The header direction must be `ltr` or `rtl`.
107 #[error("catalog direction must be ltr or rtl")]
108 Dir,
109 /// A LOCALE entry is duplicated or malformed.
110 #[error("LOCALE entry {0}: duplicate or malformed")]
111 LocaleEntry(u32),
112 /// A fallback entry names a message twice or out of range, or there are
113 /// more than 256 fallback locales.
114 #[error("fallback: {0}")]
115 Fallback(&'static str),
116 /// Beyond the format's limits (2²⁴ messages, 2³⁰ bytes of MESSAGES or
117 /// STRINGS, 2³² for counts).
118 #[error("too large: {0}")]
119 TooLarge(&'static str),
120 /// A writer bug: the two passes disagreed.
121 #[error("internal writer error: {0}")]
122 Internal(&'static str),
123}
124
125/// Why the decoder could not rebuild a message's model.
126#[cfg(feature = "decode")]
127#[derive(Clone, PartialEq, Eq, Debug, Error)]
128#[non_exhaustive]
129pub enum DecodeError {
130 /// The id is out of range or the message is absent from the catalog.
131 #[error("message is absent from the catalog")]
132 Absent,
133 /// The message's MESSAGES record is malformed.
134 #[error("malformed MESSAGES record")]
135 Malformed,
136 /// A string reference is out of bounds or not valid UTF-8.
137 #[error("invalid string reference")]
138 String,
139 /// The message's NAMES entry is missing or malformed, or a variable's
140 /// index is outside it.
141 #[error("invalid NAMES entry")]
142 Names,
143 /// A function index is outside FUNCS.
144 #[error("function index out of range")]
145 Function,
146 /// The message's COLD record is malformed.
147 #[error("malformed COLD record")]
148 Cold,
149}
150
151/// Why a manifest file (`manifest.mf2m`) or value was rejected.
152#[cfg(feature = "manifest")]
153#[derive(Clone, PartialEq, Eq, Debug, Error)]
154#[non_exhaustive]
155pub enum ManifestError {
156 /// Not a manifest file.
157 #[error("not an .mf2m manifest")]
158 Magic,
159 /// A major version this reader does not know.
160 #[error("unsupported manifest version")]
161 Version,
162 /// The file ends early, or a varint is malformed.
163 #[error("manifest is truncated or malformed")]
164 Truncated,
165 /// A name is not valid UTF-8.
166 #[error("manifest string is not UTF-8")]
167 Utf8,
168 /// The stored hash does not match the content.
169 #[error("manifest hash does not match its content")]
170 Hash,
171 /// Bytes after the end of the manifest.
172 #[error("trailing bytes after the manifest")]
173 Trailing,
174 /// A list is not strictly ascending, or the per-message lists do not
175 /// match the ids.
176 #[error("invalid manifest: {0}")]
177 Invalid(&'static str),
178}