Skip to main content

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}