nbt/
lib.rs

1//! NBT (Named Binary Tag) is a tag based binary format designed to carry large
2//! amounts of binary data with smaller amounts of additional data.
3//!
4//! # Examples
5//!
6//! ## Read
7//!
8//! ```
9//! use nbt::decode::read_compound_tag;
10//! use std::io::Cursor;
11//!
12//! let mut cursor = Cursor::new(include_bytes!("../test/binary/servers.dat").to_vec());
13//! let root_tag = read_compound_tag(&mut cursor).unwrap();
14//!
15//! let servers = root_tag.get_compound_tag_vec("servers").unwrap();
16//! assert_eq!(servers.len(), 1);
17//!
18//! let server = servers[0];
19//! let ip = server.get_str("ip").unwrap();
20//! let name = server.get_str("name").unwrap();
21//! let hide_address = server.get_bool("hideAddress").unwrap();
22//!
23//! assert_eq!(ip, "localhost:25565");
24//! assert_eq!(name, "Minecraft Server");
25//! assert!(hide_address);
26//! ```
27//!
28//! ## Write
29//!
30//! ```
31//! use nbt::encode::write_compound_tag;
32//! use nbt::CompoundTag;
33//!
34//! let mut server = CompoundTag::new();
35//!
36//! server.insert_str("ip", "localhost:25565");
37//! server.insert_str("name", "Minecraft Server");
38//! server.insert_bool("hideAddress", true);
39//!
40//! let mut servers = Vec::new();
41//! servers.push(server);
42//!
43//! let mut root_tag = CompoundTag::new();
44//! root_tag.insert_compound_tag_vec("servers", servers);
45//!
46//! // Write stringified NBT as accepted by Minecraft commands
47//! let string = root_tag.to_string();
48//! // Write NBT
49//! let mut vec = Vec::new();
50//! write_compound_tag(&mut vec, &root_tag).unwrap();
51//! ```
52use linked_hash_map::LinkedHashMap;
53use std::fmt::{Debug, Display, Formatter};
54use std::{
55    convert::{TryFrom, TryInto},
56    fmt,
57};
58
59pub mod decode;
60pub mod encode;
61
62/// Possible types of tags and they payload.
63#[derive(Debug, Clone)]
64pub enum Tag {
65    Byte(i8),
66    Short(i16),
67    Int(i32),
68    Long(i64),
69    Float(f32),
70    Double(f64),
71    ByteArray(Vec<i8>),
72    String(String),
73    List(Vec<Tag>),
74    Compound(CompoundTag),
75    IntArray(Vec<i32>),
76    LongArray(Vec<i64>),
77}
78
79impl Tag {
80    fn type_id(&self) -> u8 {
81        match self {
82            Tag::Byte(_) => 1,
83            Tag::Short(_) => 2,
84            Tag::Int(_) => 3,
85            Tag::Long(_) => 4,
86            Tag::Float(_) => 5,
87            Tag::Double(_) => 6,
88            Tag::ByteArray(_) => 7,
89            Tag::String(_) => 8,
90            Tag::List(_) => 9,
91            Tag::Compound(_) => 10,
92            Tag::IntArray(_) => 11,
93            Tag::LongArray(_) => 12,
94        }
95    }
96
97    fn type_name(&self) -> &'static str {
98        match self {
99            Tag::Byte(_) => "TAG_Byte",
100            Tag::Short(_) => "TAG_Short",
101            Tag::Int(_) => "TAG_Int",
102            Tag::Long(_) => "TAG_Long",
103            Tag::Float(_) => "TAG_Float",
104            Tag::Double(_) => "TAG_Double",
105            Tag::ByteArray(_) => "TAG_Byte_Array",
106            Tag::String(_) => "TAG_String",
107            Tag::List(_) => "TAG_List",
108            Tag::Compound(_) => "TAG_Compound",
109            Tag::IntArray(_) => "TAG_Int_Array",
110            Tag::LongArray(_) => "TAG_Long_Array",
111        }
112    }
113}
114
115macro_rules! impl_from_for_copy {
116    ($type: ty, $tag: ident) => {
117        impl From<$type> for Tag {
118            fn from(data: $type) -> Self {
119                Tag::$tag(data)
120            }
121        }
122
123        impl<'a> TryFrom<&'a Tag> for $type {
124            // Using a &'static str (tag name) of i8 (tag id) as Error would have fit better,
125            // but we need the tag as ref so we can construct a CompoundTagError
126            // when we mutably borrow a tag.
127            type Error = &'a Tag;
128
129            fn try_from(tag: &'a Tag) -> Result<Self, Self::Error> {
130                match tag {
131                    Tag::$tag(value) => Ok(*value),
132                    actual_tag => Err(actual_tag),
133                }
134            }
135        }
136
137        impl<'a> TryFrom<&'a mut Tag> for &'a mut $type {
138            type Error = &'a Tag;
139
140            fn try_from(tag: &'a mut Tag) -> Result<&mut$type, Self::Error> {
141                match tag {
142                    Tag::$tag(value) => Ok(value),
143                    actual_tag => Err(actual_tag),
144                }
145            }
146        }
147    };
148}
149
150macro_rules! impl_from_for_ref {
151    ($type: ty, $tag: ident) => {
152        impl From<$type> for Tag {
153            fn from(data: $type) -> Self {
154                Tag::$tag(data)
155            }
156        }
157
158        impl<'a> TryFrom<&'a Tag> for &'a $type {
159            type Error = &'a Tag;
160
161            fn try_from(tag: &'a Tag) -> Result<&$type, Self::Error> {
162                match tag {
163                    Tag::$tag(value) => Ok(value),
164                    actual_tag => Err(actual_tag),
165                }
166            }
167        }
168
169        impl<'a> TryFrom<&'a mut Tag> for &'a mut $type {
170            type Error = &'a Tag;
171
172            fn try_from(tag: &'a mut Tag) -> Result<&mut$type, Self::Error> {
173                match tag {
174                    Tag::$tag(value) => Ok(value),
175                    actual_tag => Err(actual_tag),
176                }
177            }
178        }
179    };
180}
181
182impl_from_for_copy!(i8, Byte);
183impl_from_for_copy!(i16, Short);
184impl_from_for_copy!(i32, Int);
185impl_from_for_copy!(i64, Long);
186impl_from_for_copy!(f32, Float);
187impl_from_for_copy!(f64, Double);
188impl_from_for_ref!(Vec<i8>, ByteArray);
189impl_from_for_ref!(String, String);
190impl_from_for_ref!(Vec<Tag>, List);
191impl_from_for_ref!(CompoundTag, Compound);
192impl_from_for_ref!(Vec<i32>, IntArray);
193impl_from_for_ref!(Vec<i64>, LongArray);
194
195#[derive(Clone)]
196pub struct CompoundTag {
197    pub name: Option<String>,
198    tags: LinkedHashMap<String, Tag>,
199}
200
201/// Possible types of errors while trying to get value from compound tag.
202#[derive(Debug)]
203pub enum CompoundTagError<'a> {
204    /// Tag with provided name not found.
205    TagNotFound {
206        /// Name of tag which was not found.
207        name: &'a str,
208    },
209    /// Tag actual type not match expected.
210    TagWrongType {
211        /// Name of tag which type not matched.
212        name: &'a str,
213        /// Actual tag.
214        actual_tag: &'a Tag,
215    },
216}
217
218impl<'a> std::error::Error for CompoundTagError<'a> {
219    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
220        None
221    }
222}
223
224impl<'a> Display for CompoundTagError<'a> {
225    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
226        match self {
227            CompoundTagError::TagNotFound { name } => write!(f, "Tag {} not found", name),
228            CompoundTagError::TagWrongType { name, actual_tag } => {
229                write!(f, "Tag {} has type {}", name, actual_tag.type_name())
230            }
231        }
232    }
233}
234
235macro_rules! define_primitive_type (
236    ($type: ident, $tag: ident, $getter_name: ident, $setter_name: ident) => (
237        pub fn $setter_name(&mut self, name: impl ToString, value: $type) {
238            self.tags.insert(name.to_string(), Tag::$tag(value));
239        }
240
241        pub fn $getter_name<'a>(&'a self, name: &'a str) -> Result<$type, CompoundTagError<'a>> {
242            match self.tags.get(name) {
243                Some(tag) => match tag {
244                    Tag::$tag(value) => Ok(*value),
245                    actual_tag => Err(CompoundTagError::TagWrongType { name, actual_tag }),
246                },
247                None => Err(CompoundTagError::TagNotFound { name }),
248            }
249        }
250   );
251);
252
253macro_rules! define_array_type (
254    ($type: ident, $tag: ident, $getter_name: ident, $setter_name: ident) => (
255        pub fn $setter_name(&mut self, name: impl ToString, value: Vec<$type>) {
256            self.tags.insert(name.to_string(), Tag::$tag(value));
257        }
258
259        pub fn $getter_name<'a>(&'a self, name: &'a str) -> Result<&Vec<$type>, CompoundTagError<'a>> {
260            match self.tags.get(name) {
261                Some(tag) => match tag {
262                    Tag::$tag(value) => Ok(value),
263                    actual_tag => Err(CompoundTagError::TagWrongType { name, actual_tag }),
264                },
265                None => Err(CompoundTagError::TagNotFound { name }),
266            }
267        }
268   );
269);
270
271macro_rules! define_list_type (
272    ($type: ident, $tag: ident, $getter_name: ident, $setter_name: ident) => (
273        pub fn $setter_name(&mut self, name: impl ToString, vec: impl IntoIterator<Item=$type>) {
274            let mut tags = Vec::new();
275
276            for value in vec {
277                tags.push(Tag::$tag(value));
278            }
279
280            self.tags.insert(name.to_string(), Tag::List(tags));
281        }
282
283        pub fn $getter_name<'a>(&'a self, name: &'a str) -> Result<Vec<$type>, CompoundTagError<'a>> {
284            let tags = self.get_vec(name)?;
285            let mut vec = Vec::new();
286
287             for tag in tags {
288                 match tag {
289                     Tag::$tag(value) => vec.push(*value),
290                     actual_tag => return Err(CompoundTagError::TagWrongType { name, actual_tag }),
291                 }
292             }
293
294             Ok(vec)
295        }
296   );
297);
298
299impl CompoundTag {
300    pub fn new() -> Self {
301        CompoundTag {
302            name: None,
303            tags: LinkedHashMap::new(),
304        }
305    }
306
307    pub fn named(name: impl ToString) -> Self {
308        CompoundTag {
309            name: Some(name.to_string()),
310            tags: LinkedHashMap::new(),
311        }
312    }
313
314    pub fn is_empty(&self) -> bool {
315        self.tags.is_empty()
316    }
317
318    pub fn contains_key(&self, name: &str) -> bool {
319        self.tags.contains_key(name)
320    }
321
322    pub fn insert(&mut self, name: impl ToString, tag: impl Into<Tag>) {
323        self.tags.insert(name.to_string(), tag.into());
324    }
325
326    pub fn get<'a, 'b: 'a, T: TryFrom<&'a Tag>>(&'a self, name: &'b str) -> Result<T, CompoundTagError> {
327        match self.tags.get(name) {
328            Some(tag) => match tag.try_into() {
329                Ok(value) => Ok(value),
330                Err(..) => Err(CompoundTagError::TagWrongType {
331                    name,
332                    actual_tag: tag,
333                }),
334            },
335            None => Err(CompoundTagError::TagNotFound { name }),
336        }
337    }
338
339    pub fn get_mut<'a, 'b, T>(&'a mut self, name: &'b str) -> Result<T, CompoundTagError>
340    where
341        'b: 'a,
342        T: TryFrom<&'a mut Tag, Error = &'a Tag>,
343    {
344        match self.tags.get_mut(name) {
345            Some(tag) => match tag.try_into() {
346                Ok(value) => Ok(value),
347                Err(actual_tag) => Err(CompoundTagError::TagWrongType { name, actual_tag }),
348            },
349            None => Err(CompoundTagError::TagNotFound { name }),
350        }
351    }
352
353    define_primitive_type!(i8, Byte, get_i8, insert_i8);
354    define_primitive_type!(i16, Short, get_i16, insert_i16);
355    define_primitive_type!(i32, Int, get_i32, insert_i32);
356    define_primitive_type!(i64, Long, get_i64, insert_i64);
357    define_primitive_type!(f32, Float, get_f32, insert_f32);
358    define_primitive_type!(f64, Double, get_f64, insert_f64);
359    define_array_type!(i8, ByteArray, get_i8_vec, insert_i8_vec);
360    define_array_type!(i32, IntArray, get_i32_vec, insert_i32_vec);
361    define_array_type!(i64, LongArray, get_i64_vec, insert_i64_vec);
362    define_list_type!(i16, Short, get_i16_vec, insert_i16_vec);
363    define_list_type!(f32, Float, get_f32_vec, insert_f32_vec);
364    define_list_type!(f64, Double, get_f64_vec, insert_f64_vec);
365
366    pub fn insert_bool(&mut self, name: &str, value: bool) {
367        if value {
368            self.insert_i8(name, 1);
369        } else {
370            self.insert_i8(name, 0);
371        }
372    }
373
374    pub fn get_bool<'a>(&'a self, name: &'a str) -> Result<bool, CompoundTagError<'a>> {
375        Ok(self.get_i8(name)? == 1)
376    }
377
378    pub fn insert_str(&mut self, name: impl ToString, value: impl ToString) {
379        self.tags
380            .insert(name.to_string(), Tag::String(value.to_string()));
381    }
382
383    pub fn get_str<'a>(&'a self, name: &'a str) -> Result<&str, CompoundTagError<'a>> {
384        match self.tags.get(name) {
385            Some(tag) => match tag {
386                Tag::String(value) => Ok(value),
387                actual_tag => Err(CompoundTagError::TagWrongType { name, actual_tag }),
388            },
389            None => Err(CompoundTagError::TagNotFound { name }),
390        }
391    }
392
393    pub fn insert_compound_tag(&mut self, name: impl ToString, value: CompoundTag) {
394        self.tags.insert(name.to_string(), Tag::Compound(value));
395    }
396
397    pub fn get_compound_tag<'a>(
398        &'a self,
399        name: &'a str,
400    ) -> Result<&CompoundTag, CompoundTagError<'a>> {
401        match self.tags.get(name) {
402            Some(tag) => match tag {
403                Tag::Compound(value) => Ok(value),
404                actual_tag => Err(CompoundTagError::TagWrongType { name, actual_tag }),
405            },
406            None => Err(CompoundTagError::TagNotFound { name }),
407        }
408    }
409
410    fn get_vec<'a>(&'a self, name: &'a str) -> Result<&Vec<Tag>, CompoundTagError<'a>> {
411        match self.tags.get(name) {
412            Some(tag) => match tag {
413                Tag::List(value) => Ok(value),
414                actual_tag => Err(CompoundTagError::TagWrongType { name, actual_tag }),
415            },
416            None => Err(CompoundTagError::TagNotFound { name }),
417        }
418    }
419
420    pub fn insert_str_vec(
421        &mut self,
422        name: impl ToString,
423        vec: impl IntoIterator<Item = impl ToString>,
424    ) {
425        let mut tags = Vec::new();
426
427        for value in vec {
428            tags.push(Tag::String(value.to_string()));
429        }
430
431        self.tags.insert(name.to_string(), Tag::List(tags));
432    }
433
434    pub fn get_str_vec<'a>(&'a self, name: &'a str) -> Result<Vec<&str>, CompoundTagError<'a>> {
435        let tags = self.get_vec(name)?;
436        let mut vec = Vec::new();
437
438        for tag in tags {
439            match tag {
440                Tag::String(value) => vec.push(value.as_str()),
441                actual_tag => return Err(CompoundTagError::TagWrongType { name, actual_tag }),
442            }
443        }
444
445        Ok(vec)
446    }
447
448    pub fn insert_compound_tag_vec(
449        &mut self,
450        name: impl ToString,
451        vec: impl IntoIterator<Item = CompoundTag>,
452    ) {
453        let mut tags = Vec::new();
454
455        for value in vec {
456            tags.push(Tag::Compound(value));
457        }
458
459        self.tags.insert(name.to_string(), Tag::List(tags));
460    }
461
462    pub fn get_compound_tag_vec<'a>(
463        &'a self,
464        name: &'a str,
465    ) -> Result<Vec<&CompoundTag>, CompoundTagError<'a>> {
466        let tags = self.get_vec(name)?;
467        let mut vec = Vec::new();
468
469        for tag in tags {
470            match tag {
471                Tag::Compound(value) => vec.push(value),
472                actual_tag => return Err(CompoundTagError::TagWrongType { name, actual_tag }),
473            }
474        }
475
476        Ok(vec)
477    }
478
479    pub fn iter(&self) -> impl DoubleEndedIterator<Item = (&String, &Tag)> {
480        self.tags.iter()
481    }
482
483    pub fn iter_mut(&mut self) -> impl DoubleEndedIterator<Item = (&String, &mut Tag)> {
484        self.tags.iter_mut()
485    }
486}
487
488pub struct IntoIter(linked_hash_map::IntoIter<String, Tag>);
489
490impl Iterator for IntoIter {
491    type Item = (String, Tag);
492
493    fn next(&mut self) -> Option<Self::Item> {
494        self.0.next()
495    }
496}
497
498impl DoubleEndedIterator for IntoIter {
499    fn next_back(&mut self) -> Option<Self::Item> {
500        self.0.next_back()
501    }
502}
503
504impl IntoIterator for CompoundTag {
505    type Item = (String, Tag);
506    type IntoIter = IntoIter;
507
508    fn into_iter(self) -> Self::IntoIter {
509        IntoIter(self.tags.into_iter())
510    }
511}
512
513impl std::iter::FromIterator<(String, Tag)> for CompoundTag {
514    fn from_iter<T: IntoIterator<Item = (String, Tag)>>(iter: T) -> Self {
515        CompoundTag {
516            name: None,
517            tags: iter.into_iter().collect(),
518        }
519    }
520}
521
522impl<'a> std::iter::FromIterator<(&'a str, Tag)> for CompoundTag {
523    fn from_iter<T: IntoIterator<Item = (&'a str, Tag)>>(iter: T) -> Self {
524        CompoundTag {
525            name: None,
526            tags: iter
527                .into_iter()
528                .map(|(name, tag)| (name.into(), tag))
529                .collect(),
530        }
531    }
532}
533
534impl Debug for CompoundTag {
535    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
536        let name_ref = self.name.as_ref().map(|x| &**x);
537        fmt_tag(f, name_ref, &Tag::Compound(self.clone()), 0)
538    }
539}
540
541fn fmt_tag(
542    f: &mut Formatter,
543    name: Option<&str>,
544    tag: &Tag,
545    indent: usize,
546) -> Result<(), fmt::Error> {
547    fmt_indent(f, indent)?;
548
549    let type_name = tag.type_name();
550
551    match tag {
552        Tag::Byte(value) => fmt_simple_tag(f, type_name, name, value)?,
553        Tag::Short(value) => fmt_simple_tag(f, type_name, name, value)?,
554        Tag::Int(value) => fmt_simple_tag(f, type_name, name, value)?,
555        Tag::Long(value) => fmt_simple_tag(f, type_name, name, value)?,
556        Tag::Float(value) => fmt_simple_tag(f, type_name, name, value)?,
557        Tag::Double(value) => fmt_simple_tag(f, type_name, name, value)?,
558        Tag::ByteArray(value) => fmt_array_tag(f, type_name, name, value)?,
559        Tag::String(value) => fmt_simple_tag(f, type_name, name, value)?,
560        Tag::List(value) => {
561            let length = value.len();
562
563            fmt_list_start(f, type_name, name, length)?;
564
565            for tag in value {
566                fmt_tag(f, None, tag, indent + 2)?;
567            }
568
569            if length > 0 {
570                fmt_list_end(f, indent)?;
571            }
572        }
573        Tag::Compound(value) => {
574            let name_ref = name.as_ref().map(|x| &**x);
575            let length = value.tags.len();
576
577            fmt_list_start(f, type_name, name_ref, length)?;
578
579            for (name, tag) in &value.tags {
580                fmt_tag(f, Some(name.as_str()), tag, indent + 2)?;
581            }
582
583            if length > 0 {
584                fmt_list_end(f, indent)?;
585            }
586        }
587        Tag::IntArray(value) => fmt_array_tag(f, type_name, name, value)?,
588        Tag::LongArray(value) => fmt_array_tag(f, type_name, name, value)?,
589    };
590
591    Ok(())
592}
593
594fn fmt_simple_tag<V: Display>(
595    f: &mut Formatter,
596    type_name: &str,
597    name: Option<&str>,
598    value: V,
599) -> Result<(), fmt::Error> {
600    writeln!(f, "{}('{}'): '{}'", type_name, fmt_str_opt(name), value)
601}
602
603fn fmt_array_tag<V: Debug>(
604    f: &mut Formatter,
605    type_name: &str,
606    name: Option<&str>,
607    value: V,
608) -> Result<(), fmt::Error> {
609    writeln!(f, "{}('{}'): '{:?}'", type_name, fmt_str_opt(name), value)
610}
611
612fn fmt_list_start(
613    f: &mut Formatter,
614    type_name: &str,
615    name: Option<&str>,
616    length: usize,
617) -> Result<(), fmt::Error> {
618    let fmt_name = fmt_str_opt(name);
619
620    match length {
621        0 => writeln!(f, "{}('{}'): 0 entries", type_name, fmt_name),
622        1 => writeln!(f, "{}('{}'): 1 entry {{", type_name, fmt_name),
623        _ => writeln!(f, "{}('{}'): {} entries {{", type_name, fmt_name, length),
624    }
625}
626
627fn fmt_list_end(f: &mut Formatter, indent: usize) -> Result<(), fmt::Error> {
628    fmt_indent(f, indent)?;
629    writeln!(f, "}}")
630}
631
632fn fmt_indent(f: &mut Formatter, indent: usize) -> Result<(), fmt::Error> {
633    write!(f, "{:indent$}", "", indent = indent)
634}
635
636fn fmt_str_opt(name: Option<&str>) -> &str {
637    match name {
638        Some(value) => value,
639        None => "",
640    }
641}
642
643impl Display for CompoundTag {
644    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
645        // Ignore self.name because it isn't accepted by Minecraft
646        // We can't use f.debug_struct() because that would use child Debug, not Display
647        write!(f, "{{")?;
648        let mut first = true;
649        for (name, value) in &self.tags {
650            write!(f, "{}{:?}:{}", if first { "" } else { "," }, name, value)?;
651            first = false;
652        }
653        write!(f, "}}")
654    }
655}
656
657// Display NBT in SNBT format
658impl Display for Tag {
659    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
660        fn format_list<T: Display>(
661            f: &mut Formatter<'_>,
662            type_header: &'static str,
663            list: &Vec<T>,
664        ) -> Result<(), fmt::Error> {
665            write!(f, "[{}", type_header)?;
666            let mut first = true;
667            for elem in list {
668                write!(f, "{}{}", if first { "" } else { "," }, elem)?;
669                first = false;
670            }
671            write!(f, "]")
672        }
673        match self {
674            Tag::Byte(data) => write!(f, "{}b", data),
675            Tag::Short(data) => write!(f, "{}s", data),
676            Tag::Int(data) => write!(f, "{}", data),
677            Tag::Long(data) => write!(f, "{}l", data),
678            Tag::Float(data) => write!(f, "{}f", data),
679            Tag::Double(data) => write!(f, "{}d", data),
680            Tag::ByteArray(data) => format_list(f, "B;", data),
681            Tag::String(data) => write!(f, "{:?}", data),
682            Tag::List(data) => format_list(f, "", data),
683            Tag::Compound(data) => write!(f, "{}", data),
684            Tag::IntArray(data) => format_list(f, "I;", data),
685            Tag::LongArray(data) => format_list(f, "L;", data),
686        }
687    }
688}
689
690#[test]
691fn test_compound_tag_i8() {
692    let mut compound_tag = CompoundTag::new();
693    compound_tag.insert_i8("i8", 1);
694
695    assert_eq!(compound_tag.get_i8("i8").unwrap(), 1i8);
696}
697
698#[test]
699fn test_compound_tag_bool() {
700    let mut compound_tag = CompoundTag::new();
701    compound_tag.insert_bool("bool", true);
702
703    assert!(compound_tag.get_bool("bool").unwrap());
704}
705
706#[test]
707fn test_compound_tag_i16() {
708    let mut compound_tag = CompoundTag::new();
709    compound_tag.insert_i16("i16", 2);
710
711    assert_eq!(compound_tag.get_i16("i16").unwrap(), 2i16);
712}
713
714#[test]
715fn test_compound_tag_i32() {
716    let mut compound_tag = CompoundTag::new();
717    compound_tag.insert_i32("i32", 3);
718
719    assert_eq!(compound_tag.get_i32("i32").unwrap(), 3i32);
720}
721
722#[test]
723fn test_compound_tag_i64() {
724    let mut compound_tag = CompoundTag::new();
725    compound_tag.insert_i64("i64", 4);
726
727    assert_eq!(compound_tag.get_i64("i64").unwrap(), 4i64);
728}
729
730#[test]
731fn test_compound_tag_f32() {
732    let mut compound_tag = CompoundTag::new();
733    compound_tag.insert_f32("f32", 5.1);
734
735    assert_eq!(compound_tag.get_f32("f32").unwrap(), 5.1f32);
736}
737
738#[test]
739fn test_compound_tag_f64() {
740    let mut compound_tag = CompoundTag::new();
741    compound_tag.insert_f64("f64", 6.3322);
742
743    assert_eq!(compound_tag.get_f64("f64").unwrap(), 6.3322f64);
744}
745
746#[test]
747fn test_compound_tag_str() {
748    let mut compound_tag = CompoundTag::new();
749    compound_tag.insert_str("str", "hello world");
750
751    assert_eq!(compound_tag.get_str("str").unwrap(), "hello world");
752}
753
754#[test]
755fn test_compound_tag_nested_compound_tag() {
756    let mut compound_tag = CompoundTag::new();
757    let mut insert_nested_compound_tag = CompoundTag::named("nested");
758    insert_nested_compound_tag.insert_i8("i8", 1);
759    insert_nested_compound_tag.insert_str("str", "hello world");
760
761    compound_tag.insert_compound_tag("nested_compound_tag", insert_nested_compound_tag);
762
763    let get_nested_compound_tag = compound_tag
764        .get_compound_tag("nested_compound_tag")
765        .unwrap();
766
767    assert_eq!(get_nested_compound_tag.get_i8("i8").unwrap(), 1i8);
768    assert_eq!(
769        get_nested_compound_tag.get_str("str").unwrap(),
770        "hello world"
771    );
772}
773
774#[test]
775fn test_compound_tag_i8_vec() {
776    let mut compound_tag = CompoundTag::new();
777    compound_tag.insert_i8_vec("i8_vec", vec![0, 1]);
778
779    let i8_vec = compound_tag.get_i8_vec("i8_vec").unwrap();
780    assert_eq!(i8_vec[0], 0);
781    assert_eq!(i8_vec[1], 1);
782}
783
784#[test]
785fn test_compound_tag_i32_vec() {
786    let mut compound_tag = CompoundTag::new();
787    compound_tag.insert_i32_vec("i32_vec", vec![7, 8, 9]);
788
789    let i32_vec = compound_tag.get_i32_vec("i32_vec").unwrap();
790
791    assert_eq!(i32_vec[0], 7i32);
792    assert_eq!(i32_vec[1], 8i32);
793    assert_eq!(i32_vec[2], 9i32);
794}
795
796#[test]
797fn test_compound_tag_i64_vec() {
798    let mut compound_tag = CompoundTag::new();
799    compound_tag.insert_i64_vec("i64_vec", vec![10, 11, 12]);
800    let i64_vec = compound_tag.get_i64_vec("i64_vec").unwrap();
801
802    assert_eq!(i64_vec[0], 10i64);
803    assert_eq!(i64_vec[1], 11i64);
804    assert_eq!(i64_vec[2], 12i64);
805}
806
807#[test]
808fn test_compound_tag_str_vec() {
809    let mut compound_tag = CompoundTag::new();
810    let insert_str_vec = vec!["a", "b", "c"];
811
812    compound_tag.insert_str_vec("str_vec", insert_str_vec);
813
814    let get_str_vec = compound_tag.get_str_vec("str_vec").unwrap();
815    assert_eq!(get_str_vec[0], "a");
816    assert_eq!(get_str_vec[1], "b");
817    assert_eq!(get_str_vec[2], "c");
818}
819
820#[test]
821fn test_compound_tag_nested_compound_tag_vec() {
822    let mut compound_tag = CompoundTag::new();
823    let mut insert_nested_compound_tag_1 = CompoundTag::new();
824    let mut insert_nested_compound_tag_2 = CompoundTag::new();
825
826    insert_nested_compound_tag_1.insert_str("str", "test");
827    insert_nested_compound_tag_2.insert_i32("i32", 222333111);
828
829    let insert_nested_compound_tag_vec =
830        vec![insert_nested_compound_tag_1, insert_nested_compound_tag_2];
831
832    compound_tag.insert_compound_tag_vec("nested_compound_tag_vec", insert_nested_compound_tag_vec);
833
834    let get_nested_compound_tag_vec = compound_tag
835        .get_compound_tag_vec("nested_compound_tag_vec")
836        .unwrap();
837
838    let get_nested_compound_tag_1 = get_nested_compound_tag_vec[0];
839    let get_nested_compound_tag_2 = get_nested_compound_tag_vec[1];
840
841    assert_eq!(get_nested_compound_tag_1.get_str("str").unwrap(), "test");
842    assert_eq!(get_nested_compound_tag_2.get_i32("i32").unwrap(), 222333111);
843}
844
845#[test]
846fn test_servers_fmt() {
847    use crate::decode::read_compound_tag;
848    use std::io::Cursor;
849
850    let mut cursor = Cursor::new(include_bytes!("../test/binary/servers.dat").to_vec());
851    let root_tag = read_compound_tag(&mut cursor).unwrap();
852
853    assert_eq!(
854        &format!("{}", root_tag),
855        include_str!("../test/text/servers.snbt")
856    );
857    assert_eq!(
858        &format!("{:?}", root_tag),
859        include_str!("../test/text/servers.txt")
860    );
861}
862
863#[test]
864fn test_hello_world_fmt() {
865    use crate::decode::read_compound_tag;
866    use std::io::Cursor;
867
868    let mut cursor = Cursor::new(include_bytes!("../test/binary/hello_world.dat").to_vec());
869    let root_tag = read_compound_tag(&mut cursor).unwrap();
870
871    assert_eq!(
872        &format!("{}", root_tag),
873        include_str!("../test/text/hello_world.snbt")
874    );
875    assert_eq!(
876        &format!("{:?}", root_tag),
877        include_str!("../test/text/hello_world.txt")
878    );
879}
880
881#[test]
882fn test_player_fmt() {
883    use crate::decode::read_gzip_compound_tag;
884    use std::io::Cursor;
885
886    let mut cursor = Cursor::new(include_bytes!("../test/binary/player.dat").to_vec());
887    let root_tag = read_gzip_compound_tag(&mut cursor).unwrap();
888
889    assert_eq!(
890        &format!("{}", root_tag),
891        include_str!("../test/text/player.snbt")
892    );
893    assert_eq!(
894        &format!("{:?}", root_tag),
895        include_str!("../test/text/player.txt")
896    );
897}
898
899#[test]
900fn test_level_fmt() {
901    use crate::decode::read_gzip_compound_tag;
902    use std::io::Cursor;
903
904    let mut cursor = Cursor::new(include_bytes!("../test/binary/level.dat").to_vec());
905    let root_tag = read_gzip_compound_tag(&mut cursor).unwrap();
906
907    assert_eq!(
908        &format!("{}", root_tag),
909        include_str!("../test/text/level.snbt")
910    );
911    assert_eq!(
912        &format!("{:?}", root_tag),
913        include_str!("../test/text/level.txt")
914    );
915}
916
917#[test]
918fn test_is_empty() {
919    let mut compound_tag = CompoundTag::new();
920    assert!(compound_tag.is_empty());
921
922    compound_tag.insert_i32("test", 123);
923    assert!(!compound_tag.is_empty());
924}
925
926#[test]
927fn test_contains_key() {
928    let mut compound_tag = CompoundTag::new();
929    assert!(!compound_tag.contains_key("test"));
930
931    compound_tag.insert_i32("test", 123);
932    assert!(compound_tag.contains_key("test"));
933    assert!(!compound_tag.contains_key("test2"));
934}
935
936#[test]
937fn test_iter() {
938    // Test from_iter
939    let mut compound: CompoundTag = vec![
940        ("test1", Tag::Int(1)),
941        ("test2", Tag::Int(2)),
942        ("test3", Tag::Int(3)),
943    ]
944    .into_iter()
945    .collect();
946
947    // Test iter
948    {
949        let mut iter = compound.iter().map(|(name, tag)| {
950            (
951                name.as_str(),
952                match tag {
953                    Tag::Int(value) => *value,
954                    _ => panic!(),
955                },
956            )
957        });
958        assert_eq!(iter.next(), Some(("test1", 1)));
959        assert_eq!(iter.next(), Some(("test2", 2)));
960        assert_eq!(iter.next(), Some(("test3", 3)));
961        assert_eq!(iter.next(), None);
962    }
963
964    // Test iter_mut
965    for (name, tag) in compound.iter_mut() {
966        if name == "test2" {
967            match tag {
968                Tag::Int(value) => *value = 10,
969                _ => panic!(),
970            }
971        }
972    }
973
974    // Test into_iter
975    {
976        let mut iter = compound.into_iter().map(|(name, tag)| {
977            (
978                name,
979                match tag {
980                    Tag::Int(value) => value,
981                    _ => panic!(),
982                },
983            )
984        });
985        assert_eq!(iter.next(), Some((String::from("test1"), 1)));
986        assert_eq!(iter.next(), Some((String::from("test2"), 10)));
987        assert_eq!(iter.next(), Some((String::from("test3"), 3)));
988        assert_eq!(iter.next(), None);
989    }
990}