1#[macro_use]
2extern crate lazy_static;
3
4mod decoder;
5mod encoder;
6#[macro_use]
7mod tag;
8
9pub use tag::*;
10
11use std::str;
12use std::ops;
13
14#[derive(Debug, PartialEq, Clone)]
15pub struct Nbt {
16 pub name: String,
17 pub tag: Tag,
18}
19
20impl Nbt {
21 pub fn new(name: String, tag: Tag) -> Self {
22 Self { name, tag }
23 }
24
25 pub fn get<I: Index>(&self, index: I) -> Option<&Tag> {
26 self.tag.get(index)
27 }
28
29 pub fn get_mut<I: Index>(&mut self, index: I) -> Option<&mut Tag> {
30 self.tag.get_mut(index)
31 }
32
33 pub fn insert<I: Index>(&mut self, index: I, value: Tag) {
34 self.tag.insert(index, value);
35 }
36}
37
38impl<I: Index> ops::Index<I> for Tag {
39 type Output = Self;
40
41 fn index(&self, index: I) -> &Self::Output {
42 match self.get(index) {
43 Some(tag) => tag,
44 None => &Tag::End,
45 }
46 }
47}
48
49impl<I: Index> ops::IndexMut<I> for Tag {
50 fn index_mut(&mut self, index: I) -> &mut Self::Output {
51 index.index_or_insert(self)
52 }
53}
54
55impl ops::Index<&str> for Nbt {
56 type Output = Tag;
57
58 fn index<'a>(&self, index: &str) -> &Self::Output {
59 &self.tag[index]
60 }
61}
62
63impl ops::IndexMut<&str> for Nbt {
64 fn index_mut(&mut self, index: &str) -> &mut Self::Output {
65 &mut self.tag[index]
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use crate::{Kind, Nbt, Tag, tag};
72 use std::io::Cursor;
73
74 fn nbt(data: &[u8]) -> Nbt {
75 let data = data.to_vec();
76 let mut data = Cursor::new(data);
77
78 let nbt = Nbt::decode(&mut data);
80
81 assert!(nbt.is_ok(), "failed to decode nbt");
82
83 let nbt = nbt.unwrap();
85
86 assert!(match nbt.tag.kind() {
87 Kind::Compound => true,
88 _ => false,
89 }, "nbt does not match expected type");
90
91 nbt
92 }
93
94 #[test]
95 fn uncompressed() {
96 nbt(include_bytes!("../examples/uncompressed.nbt"));
97 }
98
99 #[test]
100 fn compressed() {
101 nbt(include_bytes!("../examples/compressed.nbt"));
102 }
103
104 #[cfg(feature = "preserve-order")]
105 #[test]
106 fn insertion_order() {
107 let bytes = include_bytes!("../examples/uncompressed.nbt");
108 let nbt = nbt(bytes);
109 let mut new_bytes = vec![];
110
111 assert!(nbt.encode(&mut new_bytes, false).is_ok(), "failed to encode");
112 assert!(bytes.len() == new_bytes.len(), "size doesn't match");
113 assert!({
114 let len = bytes.len();
115 let mut matches = true;
116
117 for i in 0..len {
118 if bytes[i] != new_bytes[i] {
119 matches = false;
120 break;
121 }
122 }
123
124 matches
125 }, "data doesn't match")
126 }
127
128 #[test]
129 fn string() {
130 let nbt: Tag = String::from("\"\"\\'this is a test!").into();
131 assert_eq!(format!("{}", nbt), "'\"\"\\\\\\'this is a test!'");
132 }
133
134 #[test]
135 fn assignment() {
136 let mut nbt = nbt(include_bytes!("../examples/uncompressed.nbt"));
137 nbt["testing"] = tag!("[B;8b,2b]");
138
139 assert_eq!(nbt["testing"], tag!("[B;8b,2b]"));
140 }
141
142 #[test]
143 fn display() {
144 let byte_array = tag!("[B;1B,2B,6b,10b]").to_string();
145 let list = tag!("[5l,10L,20l]").to_string();
146 let compound = tag!("{name:'Jaden'}").to_string();
147
148 assert_eq!(byte_array, "[B;1b,2b,6b,10b]");
149 assert_eq!(list, "[5L,10L,20L]");
150 assert_eq!(compound, "{name:\"Jaden\"}");
151 }
152
153 #[test]
154 fn verify_value() {
155 let nbt = nbt(include_bytes!("../examples/uncompressed.nbt"));
156 let seed: i64 = (&nbt["Data"]["RandomSeed"]).into();
157 let version_name: String = (&nbt["Data"]["Version"]["Name"]).into();
158
159 assert_eq!(seed, 4443890602994873962);
160 assert_eq!(version_name, "1.14.1 Pre-Release 2");
161 }
162}