mf2_syntax/code.rs
1//! The stable diagnostic codes of `mf2-syntax` ([`mf2_model::Diagnostic::code`]).
2//!
3//! A code identifies *what* went wrong more precisely than the
4//! [`mf2_model::ErrorKind`]. Codes are stable: a code's meaning never
5//! changes, and a retired code is never reused. `0` means "no detail".
6//!
7//! | Code | Kind | Meaning |
8//! |---:|---|---|
9//! | 1 | syntax | U+0000 is not allowed anywhere in a message |
10//! | 2 | syntax | `}` in text must be escaped as `\}` |
11//! | 3 | syntax | `\` must be followed by `\`, `{`, `\|` or `}` |
12//! | 4 | syntax | a placeholder is missing its closing `}` |
13//! | 5 | syntax | a placeholder is empty (`{}`) |
14//! | 6 | syntax | a character that cannot appear here |
15//! | 7 | syntax | whitespace is required here |
16//! | 8 | syntax | a name must start here |
17//! | 9 | syntax | `=` expected (option; `.local` declaration) |
18//! | 10 | syntax | an option needs a value: a literal or a variable |
19//! | 11 | syntax | an attribute's value must be a literal |
20//! | 12 | syntax | a quoted literal is missing its closing `\|` |
21//! | 13 | syntax | not a keyword (`.input`, `.local`, `.match`) |
22//! | 14 | syntax | an expression `{…}` expected |
23//! | 15 | syntax | `.input` needs a variable expression `{$name …}` |
24//! | 16 | syntax | `.local` needs a variable `$name` |
25//! | 17 | syntax | a complex message needs a body: `{{…}}` or `.match` |
26//! | 18 | syntax | nothing may follow the body of a complex message |
27//! | 19 | syntax | a quoted pattern is missing its closing `}}` |
28//! | 20 | syntax | `.match` needs at least one selector `$name` |
29//! | 21 | syntax | `.match` needs at least one variant |
30//! | 22 | syntax | a variant needs at least one key |
31//! | 23 | syntax | a variant needs a quoted pattern `{{…}}` |
32//! | 24 | syntax | markup is not allowed in a declaration |
33//! | 25 | syntax | an expression needs an operand or a function before attributes |
34//! | 26 | syntax | the source is longer than `u32::MAX` bytes |
35//! | 101 | variant-key-mismatch | a variant's key count differs from the selector count |
36//! | 102 | missing-fallback-variant | no variant has only catch-all keys |
37//! | 103 | missing-selector-annotation | the selector does not (directly or indirectly) reference a declaration with a function |
38//! | 104 | duplicate-declaration | the variable was declared, or used, in a previous declaration |
39//! | 105 | duplicate-declaration | the declaration's own expression uses the variable it binds |
40//! | 106 | duplicate-option-name | two options of one function or markup have the same name (under NFC) |
41//! | 107 | duplicate-variant | two variants have the same keys (under NFC) |
42
43/// U+0000 is not allowed anywhere in a message.
44pub const NUL_CHARACTER: u16 = 1;
45/// `}` in text must be escaped as `\}`.
46pub const UNESCAPED_CLOSE_BRACE: u16 = 2;
47/// `\` must be followed by `\`, `{`, `|` or `}`.
48pub const INVALID_ESCAPE: u16 = 3;
49/// A placeholder is missing its closing `}`.
50pub const UNTERMINATED_PLACEHOLDER: u16 = 4;
51/// A placeholder is empty (`{}`).
52pub const EMPTY_PLACEHOLDER: u16 = 5;
53/// A character that cannot appear here.
54pub const UNEXPECTED_CHARACTER: u16 = 6;
55/// Whitespace is required here.
56pub const MISSING_WHITESPACE: u16 = 7;
57/// A name must start here.
58pub const EXPECTED_NAME: u16 = 8;
59/// `=` expected (option; `.local` declaration).
60pub const EXPECTED_EQUALS: u16 = 9;
61/// An option needs a value: a literal or a variable.
62pub const EXPECTED_OPTION_VALUE: u16 = 10;
63/// An attribute's value must be a literal.
64pub const EXPECTED_LITERAL: u16 = 11;
65/// A quoted literal is missing its closing `|`.
66pub const UNTERMINATED_QUOTED_LITERAL: u16 = 12;
67/// Not a keyword (`.input`, `.local`, `.match`).
68pub const UNKNOWN_KEYWORD: u16 = 13;
69/// An expression `{…}` expected.
70pub const EXPECTED_EXPRESSION: u16 = 14;
71/// `.input` needs a variable expression `{$name …}`.
72pub const EXPECTED_VARIABLE_EXPRESSION: u16 = 15;
73/// `.local` needs a variable `$name`.
74pub const EXPECTED_VARIABLE: u16 = 16;
75/// A complex message needs a body: `{{…}}` or `.match`.
76pub const MISSING_BODY: u16 = 17;
77/// Nothing may follow the body of a complex message.
78pub const CONTENT_AFTER_BODY: u16 = 18;
79/// A quoted pattern is missing its closing `}}`.
80pub const UNTERMINATED_QUOTED_PATTERN: u16 = 19;
81/// `.match` needs at least one selector `$name`.
82pub const EXPECTED_SELECTOR: u16 = 20;
83/// `.match` needs at least one variant.
84pub const EXPECTED_VARIANT: u16 = 21;
85/// A variant needs at least one key.
86pub const EXPECTED_KEY: u16 = 22;
87/// A variant needs a quoted pattern `{{…}}`.
88pub const EXPECTED_QUOTED_PATTERN: u16 = 23;
89/// Markup is not allowed in a declaration.
90pub const MARKUP_NOT_ALLOWED: u16 = 24;
91/// An expression needs an operand or a function before attributes.
92pub const EXPECTED_OPERAND: u16 = 25;
93/// The source is longer than `u32::MAX` bytes.
94pub const SOURCE_TOO_LONG: u16 = 26;
95
96/// A variant's key count differs from the selector count.
97pub const VARIANT_KEY_MISMATCH: u16 = 101;
98/// No variant has only catch-all keys.
99pub const MISSING_FALLBACK_VARIANT: u16 = 102;
100/// The selector does not (directly or indirectly) reference a declaration
101/// with a function.
102pub const MISSING_SELECTOR_ANNOTATION: u16 = 103;
103/// The variable was declared, or used, in a previous declaration.
104pub const DUPLICATE_DECLARATION: u16 = 104;
105/// The declaration's own expression uses the variable it binds.
106pub const SELF_REFERENCING_DECLARATION: u16 = 105;
107/// Two options of one function or markup have the same name (under NFC).
108pub const DUPLICATE_OPTION_NAME: u16 = 106;
109/// Two variants have the same keys (under NFC).
110pub const DUPLICATE_VARIANT: u16 = 107;
111
112/// A one-line description of `code`, or `None` for an unknown code.
113pub fn describe(code: u16) -> Option<&'static str> {
114 Some(match code {
115 NUL_CHARACTER => "U+0000 is not allowed anywhere in a message",
116 UNESCAPED_CLOSE_BRACE => "`}` in text must be escaped as `\\}`",
117 INVALID_ESCAPE => "`\\` must be followed by `\\`, `{`, `|` or `}`",
118 UNTERMINATED_PLACEHOLDER => "a placeholder is missing its closing `}`",
119 EMPTY_PLACEHOLDER => "a placeholder is empty (`{}`)",
120 UNEXPECTED_CHARACTER => "a character that cannot appear here",
121 MISSING_WHITESPACE => "whitespace is required here",
122 EXPECTED_NAME => "a name must start here",
123 EXPECTED_EQUALS => "`=` expected",
124 EXPECTED_OPTION_VALUE => "an option needs a value: a literal or a variable",
125 EXPECTED_LITERAL => "an attribute's value must be a literal",
126 UNTERMINATED_QUOTED_LITERAL => "a quoted literal is missing its closing `|`",
127 UNKNOWN_KEYWORD => "not a keyword (`.input`, `.local`, `.match`)",
128 EXPECTED_EXPRESSION => "an expression `{…}` expected",
129 EXPECTED_VARIABLE_EXPRESSION => "`.input` needs a variable expression `{$name …}`",
130 EXPECTED_VARIABLE => "`.local` needs a variable `$name`",
131 MISSING_BODY => "a complex message needs a body: `{{…}}` or `.match`",
132 CONTENT_AFTER_BODY => "nothing may follow the body of a complex message",
133 UNTERMINATED_QUOTED_PATTERN => "a quoted pattern is missing its closing `}}`",
134 EXPECTED_SELECTOR => "`.match` needs at least one selector `$name`",
135 EXPECTED_VARIANT => "`.match` needs at least one variant",
136 EXPECTED_KEY => "a variant needs at least one key",
137 EXPECTED_QUOTED_PATTERN => "a variant needs a quoted pattern `{{…}}`",
138 MARKUP_NOT_ALLOWED => "markup is not allowed in a declaration",
139 EXPECTED_OPERAND => "an expression needs an operand or a function before attributes",
140 SOURCE_TOO_LONG => "the source is longer than u32::MAX bytes",
141 VARIANT_KEY_MISMATCH => "a variant's key count differs from the selector count",
142 MISSING_FALLBACK_VARIANT => "no variant has only catch-all keys",
143 MISSING_SELECTOR_ANNOTATION => {
144 "the selector does not reference a declaration with a function"
145 }
146 DUPLICATE_DECLARATION => "the variable was declared, or used, in a previous declaration",
147 SELF_REFERENCING_DECLARATION => {
148 "the declaration's own expression uses the variable it binds"
149 }
150 DUPLICATE_OPTION_NAME => "two options have the same name",
151 DUPLICATE_VARIANT => "two variants have the same keys",
152 _ => return None,
153 })
154}
155
156#[cfg(test)]
157mod tests {
158 use super::describe;
159
160 #[test]
161 fn every_code_is_described_and_zero_is_not() {
162 for code in (1..=26).chain(101..=107) {
163 assert!(describe(code).is_some(), "{code}");
164 }
165 for code in [0, 27, 100, 108, u16::MAX] {
166 assert!(describe(code).is_none(), "{code}");
167 }
168 }
169}