1use std::error::Error;
2use std::fmt::{Display, Formatter};
3use std::iter::Peekable;
4use std::str::Chars;
5
6use crate::{Compound, List, Value};
7
8const STRING_MAX_LEN: usize = 32767;
9const MAX_DEPTH: usize = 512;
11
12#[derive(Debug, Clone, PartialEq, Eq, Copy)]
13pub enum SnbtErrorKind {
14 ReachEndOfStream,
15 InvalidEscapeSequence,
16 EmptyKeyInCompound,
17 ExpectColon,
18 ExpectValue,
19 ExpectComma,
20 WrongTypeInArray,
21 DifferentTypesInList,
22 LongString,
23 TrailingData,
24 DepthLimitExceeded,
25}
26
27impl Display for SnbtErrorKind {
28 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
29 use SnbtErrorKind::*;
30 match self {
31 ReachEndOfStream => write!(f, "reach end of stream"),
32 InvalidEscapeSequence => write!(f, "invalid escape sequence"),
33 EmptyKeyInCompound => write!(f, "empty key in compound"),
34 ExpectColon => write!(f, "expect colon"),
35 ExpectValue => write!(f, "expect value"),
36 ExpectComma => write!(f, "expect comma"),
37 WrongTypeInArray => write!(f, "wrong type in array"),
38 DifferentTypesInList => write!(f, "different types in list"),
39 LongString => write!(f, "long string"),
40 TrailingData => write!(f, "extra data after end"),
41 DepthLimitExceeded => write!(f, "depth limit exceeded"),
42 }
43 }
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, Copy)]
47pub struct SnbtError {
48 pub kind: SnbtErrorKind,
49 pub line: usize,
50 pub column: usize,
51}
52
53impl SnbtError {
54 pub fn new(kind: SnbtErrorKind, line: usize, column: usize) -> Self {
55 Self { kind, line, column }
56 }
57}
58
59impl Display for SnbtError {
60 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
61 write!(f, "@ {},{}: {}", self.line, self.column, self.kind)
62 }
63}
64
65impl Error for SnbtError {}
66
67type Result<T> = std::result::Result<T, SnbtError>;
68
69#[derive(Debug)]
70pub struct SnbtReader<'a> {
71 line: usize,
72 column: usize,
73 index: usize,
74 depth: usize,
75 iter: Peekable<Chars<'a>>,
76 pushed_back: Option<char>,
77}
78
79impl<'a> SnbtReader<'a> {
80 pub fn new(input: &'a str) -> Self {
81 Self {
82 line: 1,
83 column: 1,
84 index: 0,
85 depth: 0,
86 iter: input.chars().peekable(),
87 pushed_back: None,
88 }
89 }
90
91 fn check_depth<T>(&mut self, f: impl FnOnce(&mut Self) -> Result<T>) -> Result<T> {
92 if self.depth >= MAX_DEPTH {
93 Err(self.make_error(SnbtErrorKind::DepthLimitExceeded))
94 } else {
95 self.depth += 1;
96 let res = f(self);
97 self.depth -= 1;
98 res
99 }
100 }
101
102 fn make_error(&self, kind: SnbtErrorKind) -> SnbtError {
103 SnbtError::new(kind, self.line, self.column)
104 }
105
106 fn peek(&mut self) -> Result<char> {
107 if let Some(c) = self.pushed_back {
108 Ok(c)
109 } else {
110 self.iter
111 .peek()
112 .copied()
113 .ok_or_else(|| self.make_error(SnbtErrorKind::ReachEndOfStream))
114 }
115 }
116
117 fn next(&mut self) {
118 if self.pushed_back.is_some() {
119 self.pushed_back = None;
120 return;
121 }
122
123 let result = self.iter.next();
124
125 if let Some(c) = result {
126 if c == '\n' {
127 self.line += 1;
128 self.column = 1;
129 } else {
130 self.column += 1;
131 }
132 self.index += c.len_utf8();
133 }
134 }
135
136 fn push_back(&mut self, c: char) {
138 if c == '\n' {
139 self.line -= 1;
140 self.column = 1;
141 } else {
142 self.column -= 1;
143 }
144
145 self.index -= c.len_utf8();
146
147 match self.pushed_back {
148 Some(_) => panic!("Can't push back two chars"),
149 None => self.pushed_back = Some(c),
150 };
151 }
152
153 fn skip_whitespace(&mut self) {
154 loop {
155 match self.peek() {
156 Ok(c) if c.is_whitespace() => self.next(),
157 _ => break,
158 };
159 }
160 }
161
162 fn read_string(&mut self) -> Result<String> {
163 let first = self.peek()?;
164
165 let str = match first {
166 '\"' | '\'' => self.read_quoted_string(),
167 _ => self.read_unquoted_string(),
168 }?;
169
170 if str.len() > STRING_MAX_LEN {
171 return Err(self.make_error(SnbtErrorKind::LongString));
172 }
173
174 Ok(str)
175 }
176
177 fn read_unquoted_string(&mut self) -> Result<String> {
178 let mut result = String::new();
179
180 loop {
181 let input = self.peek();
182 match input {
183 Ok('a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '-' | '+' | '.') => {
184 result.push(input?);
185 self.next();
186 }
187 _ => break,
188 }
189 }
190
191 Ok(result)
192 }
193
194 fn read_quoted_string(&mut self) -> Result<String> {
195 let quote = self.peek()?;
196 self.next();
197
198 let mut result = String::new();
199 loop {
200 let input = self.peek();
201 match input {
202 Ok(c) if c == quote => {
203 self.next();
204 break;
205 }
206 Ok('\\') => {
207 self.next();
208
209 let escape = self.peek()?;
210 if escape == quote || escape == '\\' {
211 result.push(escape);
212 } else {
213 return Err(self.make_error(SnbtErrorKind::InvalidEscapeSequence));
214 }
215
216 self.next();
217 }
218 Ok(c) => {
219 result.push(c);
220 self.next();
221 }
222 Err(e) => return Err(e),
223 }
224 }
225 if result.len() > STRING_MAX_LEN {
226 return Err(self.make_error(SnbtErrorKind::LongString));
227 }
228 Ok(result)
229 }
230
231 fn parse_compound(&mut self) -> Result<Compound> {
232 self.next();
233 self.skip_whitespace();
234
235 let mut cpd = Compound::new();
236 while self.peek()? != '}' {
237 let key = self.read_string()?;
238
239 self.skip_whitespace();
240
241 if key.is_empty() {
242 return Err(self.make_error(SnbtErrorKind::EmptyKeyInCompound));
243 }
244
245 if self.peek()? != ':' {
246 return Err(self.make_error(SnbtErrorKind::ExpectColon));
247 }
248
249 self.next();
250 self.skip_whitespace();
251
252 let value = self.parse_element()?;
253
254 self.skip_whitespace();
255 if self.peek()? == ',' {
256 self.next();
257 self.skip_whitespace();
258 } else if self.peek()? != '}' {
259 return Err(self.make_error(SnbtErrorKind::ExpectComma));
260 }
261
262 cpd.insert(key, value);
263 }
264 self.next();
265 Ok(cpd)
266 }
267
268 fn continue_parse_list(&mut self) -> Result<List> {
269 self.skip_whitespace();
270
271 let mut list = List::End;
272
273 while self.peek()? != ']' {
274 let value = self.parse_element()?;
275 self.skip_whitespace();
276
277 match (&mut list, value) {
278 (list @ List::End, value) => *list = value.into(),
279 (List::Byte(l), Value::Byte(v)) => l.push(v),
280 (List::Short(l), Value::Short(v)) => l.push(v),
281 (List::Int(l), Value::Int(v)) => l.push(v),
282 (List::Long(l), Value::Long(v)) => l.push(v),
283 (List::Float(l), Value::Float(v)) => l.push(v),
284 (List::Double(l), Value::Double(v)) => l.push(v),
285 (List::ByteArray(l), Value::ByteArray(v)) => l.push(v),
286 (List::String(l), Value::String(v)) => l.push(v),
287 (List::List(l), Value::List(v)) => l.push(v),
288 (List::Compound(l), Value::Compound(v)) => l.push(v),
289 (List::IntArray(l), Value::IntArray(v)) => l.push(v),
290 (List::LongArray(l), Value::LongArray(v)) => l.push(v),
291 _ => return Err(self.make_error(SnbtErrorKind::DifferentTypesInList)),
292 }
293
294 if self.peek()? == ',' {
295 self.next();
296 self.skip_whitespace();
297 } else if self.peek()? != ']' {
298 return Err(self.make_error(SnbtErrorKind::ExpectComma));
299 }
300 }
301 self.next();
302
303 Ok(list)
304 }
305
306 fn parse_list_like(&mut self) -> Result<Value> {
307 self.next();
308
309 let type_char = self.peek()?;
310
311 let mut values = match type_char {
312 'B' => Value::ByteArray(vec![]),
313 'I' => Value::IntArray(vec![]),
314 'L' => Value::LongArray(vec![]),
315 _ => return self.check_depth(|v| Ok(v.continue_parse_list()?.into())),
316 };
317
318 self.next();
319
320 if self.peek()? != ';' {
321 self.push_back(type_char);
322 return self.check_depth(|v| Ok(v.continue_parse_list()?.into()));
323 }
324
325 self.next();
326 self.skip_whitespace();
327
328 while self.peek()? != ']' {
329 let value = self.parse_element()?;
330
331 match (&mut values, value) {
332 (Value::ByteArray(l), Value::Byte(v)) => l.push(v),
333 (Value::IntArray(l), Value::Int(v)) => l.push(v),
334 (Value::LongArray(l), Value::Long(v)) => l.push(v),
335 _ => return Err(self.make_error(SnbtErrorKind::WrongTypeInArray)),
336 }
337
338 self.skip_whitespace();
339 if self.peek()? == ',' {
340 self.next();
341 self.skip_whitespace();
342 } else if self.peek()? != ']' {
343 return Err(self.make_error(SnbtErrorKind::ExpectComma));
344 }
345 }
346
347 self.next();
348
349 Ok(values)
350 }
351
352 fn parse_primitive(&mut self) -> Result<Value> {
353 macro_rules! try_ret {
354 ($v:expr) => {{
356 match $v {
357 Ok(v) => return Ok(v.into()),
358 Err(_) => (),
359 }
360 }};
361 }
362
363 let target = self.read_unquoted_string()?;
364
365 match target
366 .bytes()
367 .last()
368 .ok_or_else(|| self.make_error(SnbtErrorKind::ExpectValue))?
369 {
370 b'b' | b'B' => try_ret!(target[..target.len() - 1].parse::<i8>()),
371 b's' | b'S' => try_ret!(target[..target.len() - 1].parse::<i16>()),
372 b'l' | b'L' => try_ret!(target[..target.len() - 1].parse::<i64>()),
373 b'f' | b'F' => try_ret!(target[..target.len() - 1].parse::<f32>()),
374 b'd' | b'D' => try_ret!(target[..target.len() - 1].parse::<f64>()),
375 _ => (),
376 }
377
378 match target.as_str() {
379 "true" => return Ok(Value::Byte(1)),
380 "false" => return Ok(Value::Byte(0)),
381 _ => {
382 try_ret!(target.parse::<i32>());
383 try_ret!(target.parse::<f64>());
384 }
385 };
386
387 if target.len() > STRING_MAX_LEN {
388 return Err(self.make_error(SnbtErrorKind::LongString));
389 }
390
391 Ok(Value::String(target))
392 }
393
394 pub fn parse_element(&mut self) -> Result<Value> {
398 self.skip_whitespace();
399
400 match self.peek()? {
401 '{' => self.check_depth(|v| Ok(v.parse_compound()?.into())),
402 '[' => self.parse_list_like(),
403 '"' | '\'' => self.read_quoted_string().map(|s| s.into()),
404 _ => self.parse_primitive(),
405 }
406 }
407
408 pub fn read(&mut self) -> Result<Value> {
409 let value = self.parse_element()?;
410
411 self.skip_whitespace();
412 if self.peek().is_ok() {
413 return Err(self.make_error(SnbtErrorKind::TrailingData));
414 }
415
416 Ok(value)
417 }
418
419 pub fn bytes_read(&self) -> usize {
423 self.index
424 }
425}
426pub fn from_snbt_str(snbt: &str) -> Result<Value> {
441 SnbtReader::new(snbt).read()
442}
443
444#[derive(Debug)]
445pub struct SnbtWriter<'a> {
446 output: &'a mut String,
447}
448
449impl<'a> SnbtWriter<'a> {
450 pub fn new(output: &'a mut String) -> Self {
451 Self { output }
452 }
453
454 fn write_string(&mut self, s: &str) {
455 let mut need_quote = false;
456 for c in s.chars() {
457 if !matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '-' | '+' | '.') {
458 need_quote = true;
459 break;
460 }
461 }
462
463 if need_quote {
464 self.output.push('"');
465 for c in s.chars() {
466 match c {
467 '"' => self.output.push_str("\\\""),
468 '\\' => self.output.push_str("\\\\"),
469 _ => self.output.push(c),
470 }
471 }
472 self.output.push('"');
473 } else {
474 self.output.push_str(s);
475 }
476 }
477
478 fn write_primitive_array<'b>(
479 &mut self,
480 prefix: &str,
481 iter: impl Iterator<Item = &'b (impl Into<Value> + 'b + Copy)>,
482 ) {
483 self.output.push('[');
484 self.output.push_str(prefix);
485
486 let mut first = true;
487
488 for v in iter {
489 if !first {
490 self.output.push(',');
491 }
492 first = false;
493
494 self.write_element(&(*v).into());
495 }
496
497 self.output.push(']');
498 }
499
500 fn write_primitive(&mut self, postfix: &str, value: impl ToString) {
501 self.output.push_str(&value.to_string());
502 self.output.push_str(postfix);
503 }
504
505 fn write_list(&mut self, list: &List) {
506 macro_rules! variant_impl {
507 ($v:expr, $handle:expr) => {{
508 self.output.push('[');
509
510 let mut first = true;
511 for v in $v.iter() {
512 if !first {
513 self.output.push(',');
514 }
515 first = false;
516 $handle(v);
517 }
518
519 self.output.push(']');
520 }};
521 }
522 #[allow(clippy::redundant_closure_call)]
523 match list {
524 List::Byte(v) => variant_impl!(v, |v| self.write_primitive("b", v)),
525 List::Short(v) => variant_impl!(v, |v| self.write_primitive("s", v)),
526 List::Int(v) => variant_impl!(v, |v| self.write_primitive("", v)),
527 List::Long(v) => variant_impl!(v, |v| self.write_primitive("l", v)),
528 List::Float(v) => variant_impl!(v, |v| self.write_primitive("f", v)),
529 List::Double(v) => variant_impl!(v, |v| self.write_primitive("d", v)),
530 List::ByteArray(v) => {
531 variant_impl!(v, |v: &Vec<i8>| self.write_primitive_array("B", v.iter()))
532 }
533 List::IntArray(v) => {
534 variant_impl!(v, |v: &Vec<i32>| self.write_primitive_array("", v.iter()))
535 }
536 List::LongArray(v) => {
537 variant_impl!(v, |v: &Vec<i64>| self.write_primitive_array("L", v.iter()))
538 }
539 List::String(v) => variant_impl!(v, |v| self.write_string(v)),
540 List::List(v) => variant_impl!(v, |v| self.write_list(v)),
541 List::Compound(v) => variant_impl!(v, |v| self.write_compound(v)),
542 List::End => self.output.push_str("[]"),
543 }
544 }
545
546 fn write_compound(&mut self, compound: &Compound) {
547 self.output.push('{');
548
549 let mut first = true;
550 for (k, v) in compound {
551 if !first {
552 self.output.push(',');
553 }
554
555 first = false;
556
557 self.write_string(k);
558 self.output.push(':');
559 self.write_element(v);
560 }
561
562 self.output.push('}');
563 }
564
565 pub fn write_element(&mut self, value: &Value) {
567 use Value::*;
568 match value {
569 Byte(v) => self.write_primitive("b", v),
570 Short(v) => self.write_primitive("s", v),
571 Int(v) => self.write_primitive("", v),
572 Long(v) => self.write_primitive("l", v),
573 Float(v) => self.write_primitive("f", v),
574 Double(v) => self.write_primitive("d", v),
575 ByteArray(v) => self.write_primitive_array("B;", v.iter()),
576 IntArray(v) => self.write_primitive_array("I;", v.iter()),
577 LongArray(v) => self.write_primitive_array("L;", v.iter()),
578 String(v) => self.write_string(v),
579 List(v) => self.write_list(v),
580 Compound(v) => self.write_compound(v),
581 }
582 }
583}
584
585pub fn to_snbt_string(value: &Value) -> String {
587 let mut output = String::new();
588 let mut writer = SnbtWriter::new(&mut output);
589
590 writer.write_element(value);
591
592 output
593}
594
595impl Display for SnbtWriter<'_> {
596 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
597 write!(f, "{}", self.output)
598 }
599}
600
601#[cfg(test)]
602mod tests {
603
604 use super::*;
605
606 #[test]
607 fn test_parse() {
608 let str = r#"
609 {
610 foo: 1,
611 'bar': 1.0,
612 "baz": 1.0f,
613 "hello'": "hello world",
614 "world": "hello\"world",
615 1.5f: 1.5d,
616 3b: 2f,
617 bool: false,
618 more: {
619 iarr: [I; 1, 2, 3],
620 larr: [L; 1L, 2L, 3L],
621 },
622 empty: [Bibabo ],
623 }
624 "#;
625
626 let value = from_snbt_str(str).unwrap();
627 let Value::Compound(cpd) = &value else {
628 unreachable!()
629 };
630
631 assert_eq!(*cpd.get("foo").unwrap(), 1_i32.into());
632 assert_eq!(*cpd.get("bar").unwrap(), 1_f64.into());
633 assert_eq!(*cpd.get("baz").unwrap(), 1_f32.into());
634 assert_eq!(*cpd.get("hello'").unwrap(), "hello world".into());
635 assert_eq!(*cpd.get("world").unwrap(), "hello\"world".into());
636 assert_eq!(*cpd.get("1.5f").unwrap(), 1.5_f64.into());
637 assert_eq!(*cpd.get("3b").unwrap(), 2_f32.into());
638 assert_eq!(*cpd.get("bool").unwrap(), 0_i8.into());
639
640 let Some(Value::Compound(more)) = cpd.get("more") else {
641 unreachable!()
642 };
643
644 assert_eq!(*more.get("iarr").unwrap(), vec![1, 2, 3].into());
645
646 assert_eq!(*more.get("larr").unwrap(), vec![1_i64, 2, 3].into());
647
648 let Value::List(List::String(list)) = cpd.get("empty").unwrap() else {
649 unreachable!()
650 };
651
652 assert_eq!(list[0], "Bibabo");
653
654 assert_eq!(
655 from_snbt_str("\"\\n\"").unwrap_err().kind,
656 SnbtErrorKind::InvalidEscapeSequence
657 );
658
659 assert_eq!(
660 from_snbt_str("[L; 1]").unwrap_err().kind,
661 SnbtErrorKind::WrongTypeInArray
662 );
663
664 assert_eq!(
665 from_snbt_str("[L; 1L, 2L, 3L").unwrap_err().kind,
666 SnbtErrorKind::ReachEndOfStream
667 );
668
669 assert_eq!(
670 from_snbt_str("[L; 1L, 2L, 3L,]dewdwe").unwrap_err().kind,
671 SnbtErrorKind::TrailingData
672 );
673
674 assert_eq!(
675 from_snbt_str("{ foo: }").unwrap_err().kind,
676 SnbtErrorKind::ExpectValue
677 );
678
679 assert_eq!(
680 from_snbt_str("{ {}, }").unwrap_err().kind,
681 SnbtErrorKind::EmptyKeyInCompound
682 );
683
684 assert_eq!(
685 from_snbt_str("{ foo 1 }").unwrap_err().kind,
686 SnbtErrorKind::ExpectColon
687 );
688
689 assert_eq!(
690 from_snbt_str("{ foo: 1 bar: 2 }").unwrap_err().kind,
691 SnbtErrorKind::ExpectComma
692 );
693
694 assert_eq!(
695 from_snbt_str("[{}, []]").unwrap_err().kind,
696 SnbtErrorKind::DifferentTypesInList
697 );
698
699 assert_eq!(
700 from_snbt_str(&String::from_utf8(vec![b'e'; 32768]).unwrap())
701 .unwrap_err()
702 .kind,
703 SnbtErrorKind::LongString
704 );
705
706 assert_eq!(
707 from_snbt_str(
708 &String::from_utf8([[b'['; MAX_DEPTH + 1], [b']'; MAX_DEPTH + 1]].concat())
709 .unwrap()
710 )
711 .unwrap_err()
712 .kind,
713 SnbtErrorKind::DepthLimitExceeded
714 );
715
716 #[cfg(feature = "preserve_order")]
717 assert_eq!(
718 to_snbt_string(&value),
719 r#"{foo:1,bar:1d,baz:1f,"hello'":"hello world",world:"hello\"world",1.5f:1.5d,3b:2f,bool:0b,more:{iarr:[I;1,2,3],larr:[L;1l,2l,3l]},empty:[Bibabo]}"#
720 );
721 }
722}