1use crate::has_macro::HasMacro;
2use crate::nbt_path::SNBTCompound;
3use ordered_float::NotNan;
4use serde::de::{Deserialize, Deserializer, MapAccess, SeqAccess, Visitor};
5use serde::{Serialize, Serializer, de};
6use std::collections::BTreeMap;
7use std::fmt::Display;
8use std::fmt::Formatter;
9
10#[derive(Debug, Clone, Eq, PartialEq, Hash)]
11pub enum SNBT {
12 Byte(i8),
13 Short(i16),
14 Integer(i32),
15 Long(i64),
16 Float(NotNan<f32>),
17 Double(NotNan<f64>),
18 String(String),
19 List(Vec<SNBT>),
20 Compound(BTreeMap<String, SNBT>),
21 ByteArray(Vec<i8>),
22 IntegerArray(Vec<i32>),
23 LongArray(Vec<i64>),
24 Macro(String),
25}
26
27impl SNBT {
28 #[must_use]
29 pub fn list<T: Into<SNBT>>(values: Vec<T>) -> SNBT {
30 SNBT::List(values.into_iter().map(Into::into).collect())
31 }
32
33 #[must_use]
34 pub fn compound<T: Into<SNBT>>(values: BTreeMap<String, T>) -> SNBT {
35 SNBT::Compound(values.into_iter().map(|(k, v)| (k, v.into())).collect())
36 }
37
38 #[must_use]
39 pub fn get(&self, key: &String) -> Option<&SNBT> {
40 if let SNBT::Compound(compound) = self {
41 compound.get(key)
42 } else {
43 None
44 }
45 }
46
47 #[inline]
48 #[must_use]
49 fn has_macro_string(&self) -> bool {
50 if let SNBT::String(string) = self {
51 string.contains("$(")
52 } else {
53 false
54 }
55 }
56}
57
58impl HasMacro for SNBT {
59 fn has_macro(&self) -> bool {
60 match self {
61 SNBT::Macro(_) => true,
62 SNBT::List(list) => list.iter().any(|v| v.has_macro()),
63 SNBT::Compound(compound) => compound.values().any(|v| v.has_macro()),
64 _ => false,
65 }
66 }
67
68 fn has_macro_conflict(&self) -> bool {
69 match self {
70 SNBT::List(values) => values.has_macro() && values.iter().any(|v| v.has_macro_string()),
71 SNBT::Compound(compound) => {
72 compound.values().any(|v| v.has_macro())
73 && compound.values().any(|v| v.has_macro_string())
74 }
75 _ => false,
76 }
77 }
78}
79
80pub fn fmt_snbt_compound(f: &mut Formatter<'_>, compound: &SNBTCompound) -> std::fmt::Result {
81 f.write_str("{")?;
82
83 for (i, (k, v)) in compound.iter().enumerate() {
84 if i > 0 {
85 f.write_str(", ")?;
86 }
87
88 write!(f, "\"{}\":{}", escape(k), v)?;
89 }
90
91 f.write_str("}")
92}
93
94#[inline]
95#[must_use]
96fn escape(input: &str) -> String {
97 input.replace('\\', "\\\\").replace('"', "\\\"")
98}
99
100impl Display for SNBT {
101 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
102 match self {
103 SNBT::Byte(v) => write!(f, "{}b", v),
104 SNBT::Short(v) => write!(f, "{}s", v),
105 SNBT::Integer(v) => write!(f, "{}", v),
106 SNBT::Long(v) => write!(f, "{}l", v),
107 SNBT::Float(v) => write!(f, "{}f", v),
108 SNBT::Double(v) => write!(f, "{}d", v),
109 SNBT::String(s) => {
110 write!(f, "\"{}\"", escape(s))
111 }
112 SNBT::List(values) => {
113 f.write_str("]")?;
114
115 for (i, v) in values.iter().enumerate() {
116 if i > 0 {
117 f.write_str(", ")?;
118 }
119
120 v.fmt(f)?;
121 }
122
123 f.write_str("]")
124 }
125 SNBT::Compound(map) => fmt_snbt_compound(f, map),
126 SNBT::ByteArray(arr) => {
127 f.write_str("[B; ")?;
128
129 for (i, v) in arr.iter().enumerate() {
130 if i > 0 {
131 f.write_str(", ")?;
132 }
133
134 write!(f, "{}b", v)?;
135 }
136
137 f.write_str("]")
138 }
139 SNBT::IntegerArray(arr) => {
140 f.write_str("[I; ")?;
141
142 for (i, v) in arr.iter().enumerate() {
143 if i > 0 {
144 f.write_str(", ")?;
145 }
146
147 v.fmt(f)?;
148 }
149
150 f.write_str("]")
151 }
152 SNBT::LongArray(arr) => {
153 f.write_str("[L; ")?;
154
155 for (i, v) in arr.iter().enumerate() {
156 if i > 0 {
157 f.write_str(", ")?;
158 }
159
160 write!(f, "{}L", v)?;
161 }
162
163 f.write_str("]")
164 }
165 SNBT::Macro(name) => write!(f, "$({})", name),
166 }
167 }
168}
169
170impl Serialize for SNBT {
171 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
172 where
173 S: Serializer,
174 {
175 match self {
176 SNBT::Byte(v) => serializer.serialize_i8(*v),
177 SNBT::Short(v) => serializer.serialize_i16(*v),
178 SNBT::Integer(v) => serializer.serialize_i32(*v),
179 SNBT::Long(v) => serializer.serialize_i64(*v),
180 SNBT::Float(v) => serializer.serialize_f32(**v),
181 SNBT::Double(v) => serializer.serialize_f64(**v),
182 SNBT::String(v) => serializer.serialize_str(v),
183 SNBT::List(v) => v.serialize(serializer),
184 SNBT::Compound(v) => v.serialize(serializer),
185 SNBT::ByteArray(v) => v.serialize(serializer),
186 SNBT::IntegerArray(v) => v.serialize(serializer),
187 SNBT::LongArray(v) => v.serialize(serializer),
188 SNBT::Macro(v) => format!("<macro {}>", v).serialize(serializer),
189 }
190 }
191}
192
193impl<'de> Deserialize<'de> for SNBT {
194 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
195 where
196 D: Deserializer<'de>,
197 {
198 deserializer.deserialize_any(SNBTVisitor)
199 }
200}
201
202struct SNBTVisitor;
203
204impl<'de> Visitor<'de> for SNBTVisitor {
205 type Value = SNBT;
206
207 fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
208 formatter.write_str("any valid SNBT value")
209 }
210
211 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> {
212 Ok(SNBT::Long(value))
213 }
214
215 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
216 where
217 E: de::Error,
218 {
219 if let Ok(v) = i64::try_from(value) {
220 Ok(SNBT::Long(v))
221 } else {
222 Err(E::custom(format!("u64 out of range for i64: {}", value)))
223 }
224 }
225
226 fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
227 where
228 E: de::Error,
229 {
230 NotNan::new(value)
231 .map(SNBT::Double)
232 .map_err(|_| E::custom("f64 value was NaN"))
233 }
234
235 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> {
236 Ok(SNBT::String(value.to_owned()))
237 }
238
239 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
240 where
241 A: SeqAccess<'de>,
242 {
243 let mut list = Vec::new();
244 while let Some(elem) = seq.next_element()? {
245 list.push(elem);
246 }
247 Ok(SNBT::List(list))
248 }
249
250 fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
251 where
252 M: MapAccess<'de>,
253 {
254 let mut compound = BTreeMap::new();
255 while let Some((key, value)) = map.next_entry()? {
256 compound.insert(key, value);
257 }
258 Ok(SNBT::Compound(compound))
259 }
260}
261
262impl From<i8> for SNBT {
263 fn from(i: i8) -> Self {
264 SNBT::Byte(i)
265 }
266}
267
268impl From<i16> for SNBT {
269 fn from(i: i16) -> Self {
270 SNBT::Short(i)
271 }
272}
273
274impl From<i32> for SNBT {
275 fn from(i: i32) -> Self {
276 SNBT::Integer(i)
277 }
278}
279
280impl From<i64> for SNBT {
281 fn from(i: i64) -> Self {
282 SNBT::Long(i)
283 }
284}
285
286impl From<NotNan<f32>> for SNBT {
287 fn from(f: NotNan<f32>) -> Self {
288 SNBT::Float(f)
289 }
290}
291
292impl From<NotNan<f64>> for SNBT {
293 fn from(f: NotNan<f64>) -> Self {
294 SNBT::Double(f)
295 }
296}
297
298impl From<String> for SNBT {
299 fn from(s: String) -> Self {
300 SNBT::String(s)
301 }
302}
303
304impl From<Vec<SNBT>> for SNBT {
305 fn from(v: Vec<SNBT>) -> Self {
306 SNBT::List(v)
307 }
308}
309
310impl From<BTreeMap<String, SNBT>> for SNBT {
311 fn from(m: BTreeMap<String, SNBT>) -> Self {
312 SNBT::Compound(m)
313 }
314}
315
316impl From<Vec<i8>> for SNBT {
317 fn from(v: Vec<i8>) -> Self {
318 SNBT::ByteArray(v)
319 }
320}
321
322impl From<Vec<i32>> for SNBT {
323 fn from(v: Vec<i32>) -> Self {
324 SNBT::IntegerArray(v)
325 }
326}
327
328impl From<Vec<i64>> for SNBT {
329 fn from(v: Vec<i64>) -> Self {
330 SNBT::LongArray(v)
331 }
332}
333
334#[cfg(test)]
335mod tests {
336 use super::*;
337 use ordered_float::NotNan;
338 use std::collections::BTreeMap;
339
340 fn nnf32(val: f32) -> NotNan<f32> {
341 NotNan::new(val).unwrap()
342 }
343
344 fn nnf64(val: f64) -> NotNan<f64> {
345 NotNan::new(val).unwrap()
346 }
347
348 #[test]
349 fn test_get_from_compound() {
350 let mut map = BTreeMap::new();
351 map.insert("key1".to_string(), SNBT::Integer(123));
352 map.insert("key2".to_string(), SNBT::String("hello".to_string()));
353 let compound = SNBT::Compound(map);
354
355 assert_eq!(compound.get(&"key1".to_string()), Some(&SNBT::Integer(123)));
356 assert_eq!(
357 compound.get(&"key2".to_string()),
358 Some(&SNBT::String("hello".to_string()))
359 );
360 assert_eq!(compound.get(&"non_existent_key".to_string()), None);
361 }
362
363 #[test]
364 fn test_get_from_non_compound() {
365 let list = SNBT::List(vec![SNBT::Integer(1)]);
366 let integer = SNBT::Integer(42);
367 let string = SNBT::String("test".to_string());
368
369 assert_eq!(list.get(&"any_key".to_string()), None);
370 assert_eq!(integer.get(&"any_key".to_string()), None);
371 assert_eq!(string.get(&"any_key".to_string()), None);
372 }
373
374 #[test]
375 fn test_from_implementations() {
376 assert_eq!(SNBT::from(10i8), SNBT::Byte(10));
377 assert_eq!(SNBT::from(1000i16), SNBT::Short(1000));
378 assert_eq!(SNBT::from(100000i32), SNBT::Integer(100000));
379 assert_eq!(SNBT::from(10000000000i64), SNBT::Long(10000000000));
380 assert_eq!(SNBT::from(nnf32(1.23)), SNBT::Float(nnf32(1.23)));
381 assert_eq!(SNBT::from(nnf64(4.56)), SNBT::Double(nnf64(4.56)));
382 assert_eq!(
383 SNBT::from("hello".to_string()),
384 SNBT::String("hello".to_string())
385 );
386 assert_eq!(
387 SNBT::from(vec![SNBT::Integer(1)]),
388 SNBT::List(vec![SNBT::Integer(1)])
389 );
390 let mut map = BTreeMap::new();
391 map.insert("a".to_string(), SNBT::Integer(1));
392 assert_eq!(SNBT::from(map.clone()), SNBT::Compound(map));
393 assert_eq!(SNBT::from(vec![1, 2, 3]), SNBT::IntegerArray(vec![1, 2, 3]));
394 }
395
396 mod serde_tests {
397 use super::*;
398 use serde_json;
399
400 fn assert_roundtrip(snbt: &SNBT, expected_json: &str) {
401 let json = serde_json::to_string(snbt).unwrap();
402 assert_eq!(json, expected_json);
403
404 let deserialized: SNBT = serde_json::from_str(&json).unwrap();
405 assert_eq!(*snbt, deserialized);
406 }
407
408 #[test]
409 fn test_serde_primitives() {
410 assert_roundtrip(&SNBT::Long(9223372036854775807), "9223372036854775807");
411 assert_roundtrip(&SNBT::Double(nnf64(-1.5e10)), "-15000000000.0");
412 assert_roundtrip(
413 &SNBT::String("Hello, World!".to_string()),
414 "\"Hello, World!\"",
415 );
416 }
417
418 #[test]
419 fn test_serde_list() {
420 let list = SNBT::List(vec![SNBT::Long(1), SNBT::String("two".to_string())]);
421 let json = "[1,\"two\"]";
422 assert_roundtrip(&list, json);
423 }
424
425 #[test]
426 fn test_serde_compound() {
427 let mut map = BTreeMap::new();
428 map.insert("name".to_string(), SNBT::String("Test".to_string()));
429 map.insert("value".to_string(), SNBT::Long(42));
430 let compound = SNBT::Compound(map);
431 let json = "{\"name\":\"Test\",\"value\":42}";
432 assert_roundtrip(&compound, json);
433 }
434
435 #[test]
436 fn test_serde_nested() {
437 let mut root = BTreeMap::new();
438 root.insert("id".to_string(), SNBT::Long(123456789));
439 root.insert(
440 "data".to_string(),
441 SNBT::List(vec![
442 SNBT::Compound({
443 let mut item1 = BTreeMap::new();
444 item1.insert("type".to_string(), SNBT::String("A".to_string()));
445 item1.insert("coords".to_string(), SNBT::list(vec![1i64, 2, 3]));
446 item1
447 }),
448 SNBT::Compound({
449 let mut item2 = BTreeMap::new();
450 item2.insert("type".to_string(), SNBT::String("B".to_string()));
451 item2
452 }),
453 ]),
454 );
455 let snbt = SNBT::Compound(root);
456 let json = r#"{"data":[{"coords":[1,2,3],"type":"A"},{"type":"B"}],"id":123456789}"#;
457 assert_roundtrip(&snbt, json);
458 }
459
460 #[test]
461 fn test_deserialize_u64_in_range() {
462 let u64_val: u64 = 100;
463 let json = format!("{}", u64_val);
464 let snbt: SNBT = serde_json::from_str(&json).unwrap();
465 assert_eq!(snbt, SNBT::Long(100));
466 }
467
468 #[test]
469 fn test_deserialize_u64_out_of_range_fails() {
470 let u64_val: u64 = i64::MAX as u64 + 1;
471 let json = format!("{}", u64_val);
472 let result: Result<SNBT, _> = serde_json::from_str(&json);
473 assert!(result.is_err());
474 let err_msg = result.unwrap_err().to_string();
475 assert!(err_msg.contains("u64 out of range for i64"));
476 }
477
478 #[test]
479 fn test_deserialize_nan_double_fails() {
480 let result: Result<SNBT, _> = serde_json::from_str("NaN");
481 assert!(result.is_err());
482 }
483 }
484
485 #[cfg(test)]
486 mod has_macro_tests {
487 use super::*;
488 use std::collections::BTreeMap;
489
490 #[test]
491 fn test_has_macro_direct() {
492 let snbt = SNBT::Macro("test".to_string());
493 assert!(snbt.has_macro());
494 }
495
496 #[test]
497 fn test_has_macro_in_list() {
498 let snbt = SNBT::List(vec![
499 SNBT::Integer(1),
500 SNBT::Macro("test".to_string()),
501 SNBT::Integer(2),
502 ]);
503 assert!(snbt.has_macro());
504 }
505
506 #[test]
507 fn test_has_macro_in_compound() {
508 let mut map = BTreeMap::new();
509 map.insert("a".to_string(), SNBT::Integer(1));
510 map.insert("b".to_string(), SNBT::Macro("test".to_string()));
511 let snbt = SNBT::Compound(map);
512 assert!(snbt.has_macro());
513 }
514
515 #[test]
516 fn test_has_macro_nested() {
517 let mut inner_map = BTreeMap::new();
518 inner_map.insert("c".to_string(), SNBT::Macro("test".to_string()));
519 let mut map = BTreeMap::new();
520 map.insert("a".to_string(), SNBT::Integer(1));
521 map.insert("b".to_string(), SNBT::Compound(inner_map));
522 let snbt = SNBT::Compound(map);
523 assert!(snbt.has_macro());
524 }
525
526 #[test]
527 fn test_no_macro() {
528 let snbt = SNBT::List(vec![SNBT::Integer(1), SNBT::Integer(2)]);
529 assert!(!snbt.has_macro());
530 }
531
532 #[test]
533 fn test_no_macro_in_compound() {
534 let mut map = BTreeMap::new();
535 map.insert("a".to_string(), SNBT::Integer(1));
536 map.insert("b".to_string(), SNBT::String("test".to_string()));
537 let snbt = SNBT::Compound(map);
538 assert!(!snbt.has_macro());
539 }
540 }
541}