1mod kind;
2mod index;
3mod parser;
4
5pub use kind::*;
6pub use index::*;
7pub use parser::*;
8
9#[cfg(feature = "preserve-order")]
10pub use indexmap::IndexMap as Map;
11#[cfg(not(feature = "preserve-order"))]
12pub use std::collections::HashMap as Map;
13
14use regex::Regex;
15use std::fmt;
16
17lazy_static! {
18 static ref SIMPLE_PATTERN: Regex = Regex::new("^[A-Za-z0-9._+-]+$").unwrap();
19}
20
21#[derive(Debug, PartialEq, Clone)]
22pub enum Tag {
23 End,
24 Byte(i8),
25 Short(i16),
26 Int(i32),
27 Long(i64),
28 Float(f32),
29 Double(f64),
30 ByteArray(Vec<i8>),
31 String(String),
32 List(Vec<Self>),
33 Compound(Map<String, Self>),
34 IntArray(Vec<i32>),
35 LongArray(Vec<i64>),
36}
37
38macro_rules! into_tag {
39 ($input:ty, $output:ident) => {
40 impl Into<Tag> for $input {
41 fn into(self) -> Tag {
42 Tag::$output(self)
43 }
44 }
45
46 impl Into<$input> for Tag {
47 fn into(self) -> $input {
48 match self {
49 Tag::$output(value) => value,
50 _ => panic!("cannot convert tag"),
51 }
52 }
53 }
54
55 impl<'b> Into<$input> for &'b Tag {
56 fn into(self) -> $input {
57 match self {
58 Tag::$output(value) => value.to_owned(),
59 _ => panic!("cannot convert tag"),
60 }
61 }
62 }
63 };
64}
65
66macro_rules! into_tags {
67 { $({ $input:ty, $output:ident }),* $(,)* } => {
68 $(into_tag!($input, $output);)*
69 };
70}
71
72into_tags! {
73 { i8, Byte },
74 { i16, Short },
75 { i32, Int },
76 { i64, Long },
77
78 { f32, Float },
79 { f64, Double },
80
81 { String, String },
82
83 { Vec<i8>, ByteArray },
84 { Vec<i32>, IntArray },
85 { Vec<i64>, LongArray },
86}
87
88impl Tag {
89 pub fn kind(&self) -> Kind {
90 match self {
91 Self::End => Kind::End,
92 Self::Byte(_) => Kind::Byte,
93 Self::Short(_) => Kind::Short,
94 Self::Int(_) => Kind::Int,
95 Self::Long(_) => Kind::Long,
96 Self::Float(_) => Kind::Float,
97 Self::Double(_) => Kind::Double,
98 Self::ByteArray(_) => Kind::ByteArray,
99 Self::String(_) => Kind::String,
100 Self::List(v) => Kind::List(
101 v.get(0)
102 .map(|tag| tag.kind().id())
103 .unwrap_or(Kind::End.id()),
104 ),
105 Self::Compound(_) => Kind::Compound,
106 Self::IntArray(_) => Kind::IntArray,
107 Self::LongArray(_) => Kind::LongArray,
108 }
109 }
110
111 pub fn get<I: Index>(&self, index: I) -> Option<&Self> {
112 index.index_into(self)
113 }
114
115 pub fn get_mut<I: Index>(&mut self, index: I) -> Option<&mut Self> {
116 index.index_into_mut(self)
117 }
118
119 pub fn insert<I: Index>(&mut self, index: I, value: Self) {
120 *index.index_or_insert(self) = value;
121 }
122}
123
124fn quote_and_escape(s: &str) -> String {
125 let mut builder = String::new();
126 let mut quote_chr = None;
127
128 for chr in s.chars() {
129 if chr == '\\' {
130 builder.push('\\');
131 } else if chr == '"' || chr == '\'' {
132 if quote_chr.is_none() {
133 quote_chr = Some(if chr == '"' { '\'' } else { '"' });
134 }
135
136 if quote_chr.is_some() && quote_chr.unwrap() == chr {
137 builder.push('\\');
138 }
139 }
140
141 builder.push(chr);
142 }
143
144 let quote_chr = quote_chr.unwrap_or('"');
145
146 builder.insert(0, quote_chr);
147 builder.push(quote_chr);
148 builder
149}
150
151impl fmt::Display for Tag {
152 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153 write!(f, "{}", match self {
154 Self::End => panic!("cannot convert end to string"),
155
156 Self::Byte(value) => format!("{}b", *value),
157 Self::Short(value) => format!("{}s", *value),
158 Self::Int(value) => format!("{}", *value),
159 Self::Long(value) => format!("{}L", *value),
160
161 Self::Float(value) => format!("{}f", *value),
162 Self::Double(value) => format!("{}d", *value),
163
164 Self::String(s) => quote_and_escape(&s),
165
166 Self::List(v) => {
167 let mut items = vec![];
168
169 for tag in v {
170 items.push(tag.to_string());
171 }
172
173 format!("[{}]", items.join(","))
174 },
175
176 Self::Compound(m) => {
177 let mut items = vec![];
178
179 for (name, tag) in m {
180 let value = tag.to_string();
181 let key = if SIMPLE_PATTERN.is_match(&name) {
182 name.clone()
183 } else {
184 quote_and_escape(&name)
185 };
186
187 items.push(format!("{}:{}", key, value));
188 }
189
190 format!("{{{}}}", items.join(","))
191 },
192
193 Self::ByteArray(v) => {
194 let mut items = vec![];
195
196 for value in v {
197 items.push(Tag::Byte(*value).to_string());
198 }
199
200 format!("[B;{}]", items.join(","))
201 },
202 Self::IntArray(v) => {
203 let mut items = vec![];
204
205 for value in v {
206 items.push(Tag::Int(*value).to_string());
207 }
208
209 format!("[I;{}]", items.join(","))
210 },
211 Self::LongArray(v) => {
212 let mut items = vec![];
213
214 for value in v {
215 items.push(Tag::Long(*value).to_string());
216 }
217
218 format!("[L;{}]", items.join(","))
219 },
220 })
221 }
222}
223
224#[macro_export]
225macro_rules! tag {
226 ($s:expr) => {
227 $crate::Nbt::parse(format!("{}", $s))
228 .expect("failed to parse tag")
229 };
230}