1use std::{error::Error, fmt};
2
3use text_components::TextComponent;
4
5use crate::translations;
6
7#[derive(Clone, Debug, PartialEq, Eq)]
9pub struct SnbtError {
10 cursor: usize,
11 kind: SnbtErrorKind,
12}
13
14impl SnbtError {
15 pub(super) const fn new(cursor: usize, kind: SnbtErrorKind) -> Self {
16 Self { cursor, kind }
17 }
18
19 #[must_use]
21 pub const fn cursor(&self) -> usize {
22 self.cursor
23 }
24
25 #[must_use]
27 pub const fn kind(&self) -> &SnbtErrorKind {
28 &self.kind
29 }
30
31 #[must_use]
33 pub fn into_kind(self) -> SnbtErrorKind {
34 self.kind
35 }
36
37 #[must_use]
39 pub fn component(&self) -> TextComponent {
40 self.kind.component()
41 }
42}
43
44impl fmt::Display for SnbtError {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 write!(f, "SNBT parse error at byte {}: {}", self.cursor, self.kind)
47 }
48}
49
50impl Error for SnbtError {}
51
52#[non_exhaustive]
54#[derive(Clone, Debug, PartialEq, Eq)]
55pub enum SnbtErrorKind {
56 TrailingData,
58 ExpectedSymbol(char),
60 ExpectedValue,
62 ExpectedKey,
64 EmptyKey,
66 ExpectedArrayElement,
68 InvalidArrayElementType,
70 ExpectedNumberOrBoolean,
72 ExpectedStringUuid,
74 UnknownOperation {
76 name: String,
78 argument_count: usize,
80 },
81 ExpectedNumber,
83 ExpectedBinaryNumeral,
85 ExpectedDecimalNumeral,
87 ExpectedHexNumeral,
89 ExpectedQuotedString,
91 UnclosedQuotedString,
93 UnclosedEscapeSequence,
95 InvalidEscape(char),
97 ExpectedHexEscape {
99 digits: usize,
101 },
102 InvalidCodepoint(u32),
104 ExpectedCharacterName,
106 UnclosedCharacterName,
108 InvalidCharacterName(String),
110 ExpectedUnquotedString,
112 InvalidFloatingPoint,
114 NonFiniteNumber,
116 InvalidUnderscore,
118 InvalidInteger,
120 LeadingZero,
122 ExpectedNonNegativeNumber,
124 IntegerTooLarge,
126 NumberOutOfRange {
128 number_type: SnbtNumberType,
130 unsigned: bool,
132 },
133 InvalidNumber,
135}
136
137impl SnbtErrorKind {
138 #[must_use]
140 pub fn component(&self) -> TextComponent {
141 match self {
142 Self::TrailingData => TextComponent::from(&translations::ARGUMENT_NBT_TRAILING),
143 Self::ExpectedSymbol(symbol) => translations::ARGUMENT_LITERAL_INCORRECT
144 .message([symbol.to_string()])
145 .component(),
146 Self::ExpectedValue | Self::ExpectedUnquotedString => {
147 TextComponent::from(&translations::SNBT_PARSER_EXPECTED_UNQUOTED_STRING)
148 }
149 Self::ExpectedKey | Self::ExpectedQuotedString => {
150 translations::ARGUMENT_LITERAL_INCORRECT
151 .message(["\""])
152 .component()
153 }
154 Self::EmptyKey => TextComponent::from(&translations::SNBT_PARSER_EMPTY_KEY),
155 Self::ExpectedNumber => translations::ARGUMENT_LITERAL_INCORRECT
156 .message(["+"])
157 .component(),
158 Self::ExpectedBinaryNumeral => {
159 TextComponent::from(&translations::SNBT_PARSER_EXPECTED_BINARY_NUMERAL)
160 }
161 Self::ExpectedDecimalNumeral => {
162 TextComponent::from(&translations::SNBT_PARSER_EXPECTED_DECIMAL_NUMERAL)
163 }
164 Self::ExpectedHexNumeral => {
165 TextComponent::from(&translations::SNBT_PARSER_EXPECTED_HEX_NUMERAL)
166 }
167 Self::ExpectedArrayElement => {
168 TextComponent::from(&translations::SNBT_PARSER_EXPECTED_INTEGER_TYPE)
169 }
170 Self::InvalidArrayElementType => {
171 TextComponent::from(&translations::SNBT_PARSER_INVALID_ARRAY_ELEMENT_TYPE)
172 }
173 Self::ExpectedNumberOrBoolean => {
174 TextComponent::from(&translations::SNBT_PARSER_EXPECTED_NUMBER_OR_BOOLEAN)
175 }
176 Self::ExpectedStringUuid => {
177 TextComponent::from(&translations::SNBT_PARSER_EXPECTED_STRING_UUID)
178 }
179 Self::UnknownOperation {
180 name,
181 argument_count,
182 } => translations::SNBT_PARSER_NO_SUCH_OPERATION
183 .message([format!("{name}/{argument_count}")])
184 .component(),
185 Self::UnclosedQuotedString => {
186 TextComponent::from(&translations::SNBT_PARSER_INVALID_STRING_CONTENTS)
187 }
188 Self::UnclosedEscapeSequence | Self::InvalidEscape(_) => {
189 translations::ARGUMENT_LITERAL_INCORRECT
190 .message(["b"])
191 .component()
192 }
193 Self::ExpectedCharacterName => translations::ARGUMENT_LITERAL_INCORRECT
194 .message(["{"])
195 .component(),
196 Self::UnclosedCharacterName => translations::ARGUMENT_LITERAL_INCORRECT
197 .message(["}"])
198 .component(),
199 Self::ExpectedHexEscape { digits } => translations::SNBT_PARSER_EXPECTED_HEX_ESCAPE
200 .message([digits.to_string()])
201 .component(),
202 Self::InvalidCodepoint(codepoint) => translations::SNBT_PARSER_INVALID_CODEPOINT
203 .message([format!("U+{codepoint:08X}")])
204 .component(),
205 Self::InvalidCharacterName(_) => {
206 TextComponent::from(&translations::SNBT_PARSER_INVALID_CHARACTER_NAME)
207 }
208 Self::NonFiniteNumber => {
209 TextComponent::from(&translations::SNBT_PARSER_INFINITY_NOT_ALLOWED)
210 }
211 Self::InvalidUnderscore => {
213 TextComponent::from(&translations::SNBT_PARSER_UNDESCORE_NOT_ALLOWED)
214 }
215 Self::LeadingZero => {
216 TextComponent::from(&translations::SNBT_PARSER_LEADING_ZERO_NOT_ALLOWED)
217 }
218 Self::ExpectedNonNegativeNumber => {
219 TextComponent::from(&translations::SNBT_PARSER_EXPECTED_NON_NEGATIVE_NUMBER)
220 }
221 Self::InvalidFloatingPoint
222 | Self::InvalidInteger
223 | Self::IntegerTooLarge
224 | Self::NumberOutOfRange { .. }
225 | Self::InvalidNumber => translations::SNBT_PARSER_NUMBER_PARSE_FAILURE
226 .message([self.to_string()])
227 .component(),
228 }
229 }
230}
231
232impl fmt::Display for SnbtErrorKind {
233 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
234 match self {
235 Self::TrailingData => formatter.write_str("trailing data"),
236 Self::ExpectedSymbol(symbol) => write!(formatter, "expected '{symbol}'"),
237 Self::ExpectedValue => formatter.write_str("expected tag"),
238 Self::ExpectedKey => formatter.write_str("expected compound key"),
239 Self::EmptyKey => formatter.write_str("compound key cannot be empty"),
240 Self::ExpectedArrayElement => formatter.write_str("expected integer array element"),
241 Self::InvalidArrayElementType => {
242 formatter.write_str("invalid typed array element width")
243 }
244 Self::ExpectedNumberOrBoolean => formatter.write_str("bool expects a numeric tag"),
245 Self::ExpectedStringUuid => formatter.write_str("uuid expects a valid string tag"),
246 Self::UnknownOperation {
247 name,
248 argument_count,
249 } => write!(
250 formatter,
251 "unknown SNBT operation '{name}/{argument_count}'"
252 ),
253 Self::ExpectedNumber => formatter.write_str("expected number"),
254 Self::ExpectedBinaryNumeral => formatter.write_str("expected binary numeral"),
255 Self::ExpectedDecimalNumeral => formatter.write_str("expected decimal numeral"),
256 Self::ExpectedHexNumeral => formatter.write_str("expected hexadecimal numeral"),
257 Self::ExpectedQuotedString => formatter.write_str("expected quoted string"),
258 Self::UnclosedQuotedString => formatter.write_str("unclosed quoted string"),
259 Self::UnclosedEscapeSequence => formatter.write_str("unclosed escape sequence"),
260 Self::InvalidEscape(character) => {
261 write!(formatter, "invalid escape '\\{character}'")
262 }
263 Self::ExpectedHexEscape { digits } => {
264 write!(formatter, "expected {digits} hexadecimal escape digits")
265 }
266 Self::InvalidCodepoint(codepoint) => {
267 write!(formatter, "invalid Unicode code point U+{codepoint:08X}")
268 }
269 Self::ExpectedCharacterName => formatter.write_str("expected Unicode character name"),
270 Self::UnclosedCharacterName => formatter.write_str("unclosed Unicode character name"),
271 Self::InvalidCharacterName(name) => {
272 write!(formatter, "unknown Unicode name '{name}'")
273 }
274 Self::ExpectedUnquotedString => formatter.write_str("expected unquoted string"),
275 Self::InvalidFloatingPoint => formatter.write_str("invalid floating-point literal"),
276 Self::NonFiniteNumber => formatter.write_str("floating-point literal must be finite"),
277 Self::InvalidUnderscore => {
278 formatter.write_str("invalid underscore placement in number literal")
279 }
280 Self::InvalidInteger => formatter.write_str("invalid integer literal"),
281 Self::LeadingZero => formatter.write_str("integer literal cannot have leading zeroes"),
282 Self::ExpectedNonNegativeNumber => {
283 formatter.write_str("unsigned integer literal cannot be negative")
284 }
285 Self::IntegerTooLarge => formatter.write_str("integer literal is too large"),
286 Self::NumberOutOfRange {
287 number_type,
288 unsigned,
289 } => {
290 if *unsigned {
291 write!(formatter, "unsigned {number_type} literal is out of range")
292 } else {
293 write!(formatter, "{number_type} literal is out of range")
294 }
295 }
296 Self::InvalidNumber => formatter.write_str("invalid number literal"),
297 }
298 }
299}
300
301#[non_exhaustive]
303#[derive(Clone, Copy, Debug, PartialEq, Eq)]
304pub enum SnbtNumberType {
305 Byte,
307 Short,
309 Int,
311 Long,
313}
314
315impl fmt::Display for SnbtNumberType {
316 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
317 formatter.write_str(match self {
318 Self::Byte => "byte",
319 Self::Short => "short",
320 Self::Int => "int",
321 Self::Long => "long",
322 })
323 }
324}