1use std::collections::BTreeMap;
2use std::io::Cursor;
3
4use serde::de::DeserializeOwned;
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6use serde_value::Value as SerdeValue;
7
8use crate::config::NbtReadConfig;
9use crate::encoding::{BigEndian, LittleEndian, NetworkLittleEndian};
10use crate::error::{Error, Result};
11use crate::root::{read_tag_with_config, write_tag, RootTag};
12use crate::tag::{CompoundTag, ListTag, Tag, TagType};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct SerdeBehaviorContract {
23 pub option_some_as_inner_tag: bool,
24 pub option_none_rejected: bool,
25 pub vec_u8_non_empty_as_byte_array: bool,
26 pub empty_vec_u8_requires_wrapper: bool,
27}
28
29pub const SERDE_BEHAVIOR_CONTRACT: SerdeBehaviorContract = SerdeBehaviorContract {
30 option_some_as_inner_tag: true,
31 option_none_rejected: true,
32 vec_u8_non_empty_as_byte_array: true,
33 empty_vec_u8_requires_wrapper: true,
34};
35
36#[derive(Debug, Clone, PartialEq, Eq, Default)]
39pub struct NbtByteArray(pub Vec<u8>);
40
41impl Serialize for NbtByteArray {
42 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
43 where
44 S: Serializer,
45 {
46 serializer.serialize_bytes(&self.0)
47 }
48}
49
50impl<'de> Deserialize<'de> for NbtByteArray {
51 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
52 where
53 D: Deserializer<'de>,
54 {
55 Ok(Self(Vec::<u8>::deserialize(deserializer)?))
56 }
57}
58
59pub fn to_byte_array_tag(bytes: impl Into<Vec<u8>>) -> Tag {
60 Tag::ByteArray(bytes.into())
61}
62
63pub fn from_byte_array_tag(tag: &Tag) -> Result<Vec<u8>> {
64 match tag {
65 Tag::ByteArray(value) => Ok(value.clone()),
66 other => Err(Error::UnexpectedType {
67 context: "byte_array_tag_decode",
68 expected_id: TagType::ByteArray.id(),
69 actual_id: other.tag_type().id(),
70 }),
71 }
72}
73
74pub fn to_tag<T: Serialize>(value: &T) -> Result<Tag> {
75 let raw = serde_value::to_value(value).map_err(serde_error)?;
76 serde_value_to_tag(raw)
77}
78
79pub fn from_tag<T: DeserializeOwned>(tag: &Tag) -> Result<T> {
80 let raw = tag_to_serde_value(tag)?;
81 raw.deserialize_into().map_err(serde_error)
82}
83
84pub fn to_root_tag<T: Serialize>(name: impl Into<String>, value: &T) -> Result<RootTag> {
85 Ok(RootTag::new(name, to_tag(value)?))
86}
87
88pub fn from_root_tag<T: DeserializeOwned>(root: &RootTag) -> Result<T> {
89 from_tag(&root.payload)
90}
91
92pub fn to_be_bytes<T: Serialize>(value: &T) -> Result<Vec<u8>> {
93 to_be_bytes_named("", value)
94}
95
96pub fn to_le_bytes<T: Serialize>(value: &T) -> Result<Vec<u8>> {
97 to_le_bytes_named("", value)
98}
99
100pub fn to_net_bytes<T: Serialize>(value: &T) -> Result<Vec<u8>> {
101 to_net_bytes_named("", value)
102}
103
104pub fn to_be_bytes_named<T: Serialize>(name: impl Into<String>, value: &T) -> Result<Vec<u8>> {
105 let root = to_root_tag(name, value)?;
106 let mut out = Vec::new();
107 write_tag::<BigEndian, _>(&mut out, &root)?;
108 Ok(out)
109}
110
111pub fn to_le_bytes_named<T: Serialize>(name: impl Into<String>, value: &T) -> Result<Vec<u8>> {
112 let root = to_root_tag(name, value)?;
113 let mut out = Vec::new();
114 write_tag::<LittleEndian, _>(&mut out, &root)?;
115 Ok(out)
116}
117
118pub fn to_net_bytes_named<T: Serialize>(name: impl Into<String>, value: &T) -> Result<Vec<u8>> {
119 let root = to_root_tag(name, value)?;
120 let mut out = Vec::new();
121 write_tag::<NetworkLittleEndian, _>(&mut out, &root)?;
122 Ok(out)
123}
124
125pub fn from_be_bytes<T: DeserializeOwned>(bytes: &[u8]) -> Result<T> {
126 from_be_bytes_with_config(bytes, &NbtReadConfig::default())
127}
128
129pub fn from_be_bytes_named<T: DeserializeOwned>(bytes: &[u8]) -> Result<(String, T)> {
130 from_be_bytes_named_with_config(bytes, &NbtReadConfig::default())
131}
132
133pub fn from_le_bytes<T: DeserializeOwned>(bytes: &[u8]) -> Result<T> {
134 from_le_bytes_with_config(bytes, &NbtReadConfig::default())
135}
136
137pub fn from_net_bytes<T: DeserializeOwned>(bytes: &[u8]) -> Result<T> {
138 from_net_bytes_with_config(bytes, &NbtReadConfig::default())
139}
140
141pub fn from_be_bytes_with_config<T: DeserializeOwned>(
142 bytes: &[u8],
143 config: &NbtReadConfig,
144) -> Result<T> {
145 let mut cursor = Cursor::new(bytes);
146 let root = read_tag_with_config::<BigEndian, _>(&mut cursor, config)?;
147 ensure_consumed(bytes.len(), cursor.position() as usize)?;
148 from_root_tag(&root)
149}
150
151pub fn from_be_bytes_named_with_config<T: DeserializeOwned>(
152 bytes: &[u8],
153 config: &NbtReadConfig,
154) -> Result<(String, T)> {
155 let mut cursor = Cursor::new(bytes);
156 let root = read_tag_with_config::<BigEndian, _>(&mut cursor, config)?;
157 ensure_consumed(bytes.len(), cursor.position() as usize)?;
158 let value = from_root_tag(&root)?;
159 Ok((root.name, value))
160}
161
162pub fn from_le_bytes_with_config<T: DeserializeOwned>(
163 bytes: &[u8],
164 config: &NbtReadConfig,
165) -> Result<T> {
166 let mut cursor = Cursor::new(bytes);
167 let root = read_tag_with_config::<LittleEndian, _>(&mut cursor, config)?;
168 ensure_consumed(bytes.len(), cursor.position() as usize)?;
169 from_root_tag(&root)
170}
171
172pub fn from_net_bytes_with_config<T: DeserializeOwned>(
173 bytes: &[u8],
174 config: &NbtReadConfig,
175) -> Result<T> {
176 let mut cursor = Cursor::new(bytes);
177 let root = read_tag_with_config::<NetworkLittleEndian, _>(&mut cursor, config)?;
178 ensure_consumed(bytes.len(), cursor.position() as usize)?;
179 from_root_tag(&root)
180}
181
182fn ensure_consumed(total: usize, consumed: usize) -> Result<()> {
183 if consumed == total {
184 return Ok(());
185 }
186 Err(Error::TrailingPayloadBytes {
187 unread: total - consumed,
188 })
189}
190
191fn serde_error<E: std::fmt::Display>(error: E) -> Error {
192 Error::Serde {
193 message: error.to_string(),
194 }
195}
196
197fn serde_value_to_tag(value: SerdeValue) -> Result<Tag> {
198 match value {
199 SerdeValue::Bool(value) => Ok(Tag::Byte(if value { 1 } else { 0 })),
200 SerdeValue::I8(value) => Ok(Tag::Byte(value)),
201 SerdeValue::I16(value) => Ok(Tag::Short(value)),
202 SerdeValue::I32(value) => Ok(Tag::Int(value)),
203 SerdeValue::I64(value) => Ok(Tag::Long(value)),
204 SerdeValue::U8(value) => Ok(Tag::Short(value as i16)),
205 SerdeValue::U16(value) => i16::try_from(value)
206 .map(Tag::Short)
207 .or_else(|_| Ok(Tag::Int(i32::from(value)))),
208 SerdeValue::U32(value) => {
209 if let Ok(int) = i32::try_from(value) {
210 Ok(Tag::Int(int))
211 } else {
212 Ok(Tag::Long(i64::from(value)))
213 }
214 }
215 SerdeValue::U64(value) => {
216 let long = i64::try_from(value).map_err(|_| serde_error("u64 out of i64 range"))?;
217 Ok(Tag::Long(long))
218 }
219 SerdeValue::F32(value) => Ok(Tag::Float(value)),
220 SerdeValue::F64(value) => Ok(Tag::Double(value)),
221 SerdeValue::Char(value) => Ok(Tag::String(value.to_string())),
222 SerdeValue::String(value) => Ok(Tag::String(value)),
223 SerdeValue::Bytes(bytes) => Ok(Tag::ByteArray(bytes)),
224 SerdeValue::Seq(values) => serde_seq_to_tag(values),
225 SerdeValue::Map(values) => serde_map_to_compound(values).map(Tag::Compound),
226 SerdeValue::Option(None) => Err(serde_error("Option::None is not representable in NBT")),
227 SerdeValue::Option(Some(inner)) => serde_value_to_tag(*inner),
228 SerdeValue::Unit => Err(serde_error("unit values are not representable in NBT")),
229 SerdeValue::Newtype(inner) => serde_value_to_tag(*inner),
230 }
231}
232
233fn serde_seq_to_tag(values: Vec<SerdeValue>) -> Result<Tag> {
234 if values.is_empty() {
235 return Ok(Tag::List(ListTag::empty(TagType::End)));
236 }
237
238 if let Some(bytes) = try_u8_seq_to_byte_array(&values) {
239 return Ok(Tag::ByteArray(bytes));
240 }
241 if let Some(ints) = try_i32_seq_to_int_array(&values) {
242 return Ok(Tag::IntArray(ints));
243 }
244 if let Some(longs) = try_i64_seq_to_long_array(&values) {
245 return Ok(Tag::LongArray(longs));
246 }
247
248 let mut tags = Vec::with_capacity(values.len());
249 for value in values {
250 tags.push(serde_value_to_tag(value)?);
251 }
252 let element_type = tags.first().map(Tag::tag_type).unwrap_or(TagType::End);
253 Ok(Tag::List(ListTag::new(element_type, tags)?))
254}
255
256fn try_u8_seq_to_byte_array(values: &[SerdeValue]) -> Option<Vec<u8>> {
257 let mut out = Vec::with_capacity(values.len());
258 for value in values {
259 match value {
260 SerdeValue::U8(byte) => out.push(*byte),
261 _ => return None,
262 }
263 }
264 Some(out)
265}
266
267fn try_i32_seq_to_int_array(values: &[SerdeValue]) -> Option<Vec<i32>> {
268 let mut out = Vec::with_capacity(values.len());
269 for value in values {
270 match value {
271 SerdeValue::I32(int) => out.push(*int),
272 SerdeValue::U32(int) => out.push(i32::try_from(*int).ok()?),
273 _ => return None,
274 }
275 }
276 Some(out)
277}
278
279fn try_i64_seq_to_long_array(values: &[SerdeValue]) -> Option<Vec<i64>> {
280 let mut out = Vec::with_capacity(values.len());
281 for value in values {
282 match value {
283 SerdeValue::I64(long) => out.push(*long),
284 SerdeValue::U64(long) => out.push(i64::try_from(*long).ok()?),
285 _ => return None,
286 }
287 }
288 Some(out)
289}
290
291fn serde_map_to_compound(values: BTreeMap<SerdeValue, SerdeValue>) -> Result<CompoundTag> {
292 let mut out = CompoundTag::new();
293 for (key, value) in values {
294 let key = serde_key_to_string(key)?;
295 let value = serde_value_to_tag(value)?;
296 out.insert(key, value);
297 }
298 Ok(out)
299}
300
301fn serde_key_to_string(value: SerdeValue) -> Result<String> {
302 match value {
303 SerdeValue::String(value) => Ok(value),
304 SerdeValue::Char(value) => Ok(value.to_string()),
305 SerdeValue::Bool(value) => Ok(value.to_string()),
306 SerdeValue::I8(value) => Ok(value.to_string()),
307 SerdeValue::I16(value) => Ok(value.to_string()),
308 SerdeValue::I32(value) => Ok(value.to_string()),
309 SerdeValue::I64(value) => Ok(value.to_string()),
310 SerdeValue::U8(value) => Ok(value.to_string()),
311 SerdeValue::U16(value) => Ok(value.to_string()),
312 SerdeValue::U32(value) => Ok(value.to_string()),
313 SerdeValue::U64(value) => Ok(value.to_string()),
314 _ => Err(serde_error("map key must be string-like for NBT compound")),
315 }
316}
317
318fn tag_to_serde_value(tag: &Tag) -> Result<SerdeValue> {
319 match tag {
320 Tag::End => Err(serde_error(
321 "TAG_End is not representable as a typed serde value",
322 )),
323 Tag::Byte(value) => Ok(SerdeValue::I8(*value)),
324 Tag::Short(value) => Ok(SerdeValue::I16(*value)),
325 Tag::Int(value) => Ok(SerdeValue::I32(*value)),
326 Tag::Long(value) => Ok(SerdeValue::I64(*value)),
327 Tag::Float(value) => Ok(SerdeValue::F32(*value)),
328 Tag::Double(value) => Ok(SerdeValue::F64(*value)),
329 Tag::ByteArray(values) => Ok(SerdeValue::Seq(
330 values.iter().copied().map(SerdeValue::U8).collect(),
331 )),
332 Tag::String(value) => Ok(SerdeValue::String(value.clone())),
333 Tag::List(list) => {
334 let mut values = Vec::with_capacity(list.elements.len());
335 for element in &list.elements {
336 values.push(tag_to_serde_value(element)?);
337 }
338 Ok(SerdeValue::Seq(values))
339 }
340 Tag::Compound(values) => {
341 let mut map = BTreeMap::new();
342 for (key, value) in values {
343 map.insert(SerdeValue::String(key.clone()), tag_to_serde_value(value)?);
344 }
345 Ok(SerdeValue::Map(map))
346 }
347 Tag::IntArray(values) => Ok(SerdeValue::Seq(
348 values.iter().copied().map(SerdeValue::I32).collect(),
349 )),
350 Tag::LongArray(values) => Ok(SerdeValue::Seq(
351 values.iter().copied().map(SerdeValue::I64).collect(),
352 )),
353 }
354}
355
356#[cfg(test)]
357mod tests {
358 use indexmap::IndexMap;
359 use serde::{Deserialize, Serialize};
360
361 use super::*;
362
363 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
364 struct DemoData {
365 name: String,
366 health: i32,
367 pitch: f32,
368 bytes: Vec<u8>,
369 scores: Vec<i32>,
370 }
371
372 fn sample() -> DemoData {
373 DemoData {
374 name: "Steve".to_string(),
375 health: 20,
376 pitch: 11.5,
377 bytes: vec![1, 2, 3, 250],
378 scores: vec![7, 11, 42],
379 }
380 }
381
382 #[test]
383 fn tag_roundtrip_typed() {
384 let input = sample();
385 let tag = to_tag(&input).unwrap();
386 let output: DemoData = from_tag(&tag).unwrap();
387 assert_eq!(output, input);
388 }
389
390 #[test]
391 fn be_bytes_roundtrip_typed() {
392 let input = sample();
393 let bytes = to_be_bytes(&input).unwrap();
394 let output: DemoData = from_be_bytes(&bytes).unwrap();
395 assert_eq!(output, input);
396 }
397
398 #[test]
399 fn be_bytes_named_roundtrip_typed() {
400 let input = sample();
401 let bytes = to_be_bytes_named("PlayerData", &input).unwrap();
402 let (root_name, output): (String, DemoData) = from_be_bytes_named(&bytes).unwrap();
403 assert_eq!(root_name, "PlayerData");
404 assert_eq!(output, input);
405 }
406
407 #[test]
408 fn le_bytes_roundtrip_typed() {
409 let input = sample();
410 let bytes = to_le_bytes_named("demo", &input).unwrap();
411 let output: DemoData = from_le_bytes(&bytes).unwrap();
412 assert_eq!(output, input);
413 }
414
415 #[test]
416 fn net_bytes_roundtrip_typed() {
417 let input = sample();
418 let bytes = to_net_bytes(&input).unwrap();
419 let output: DemoData = from_net_bytes(&bytes).unwrap();
420 assert_eq!(output, input);
421 }
422
423 #[test]
424 fn none_option_is_rejected() {
425 #[derive(Serialize)]
426 struct OptionalField {
427 maybe: Option<i32>,
428 }
429
430 let err = to_tag(&OptionalField { maybe: None }).unwrap_err();
431 assert!(matches!(err.innermost(), Error::Serde { .. }));
432 }
433
434 #[test]
435 fn some_option_is_serialized_as_inner_tag() {
436 #[derive(Serialize, Deserialize, Debug, PartialEq)]
437 struct OptionalField {
438 maybe: Option<i32>,
439 }
440
441 let tag = to_tag(&OptionalField { maybe: Some(42) }).unwrap();
442 let compound = match tag {
443 Tag::Compound(value) => value,
444 other => panic!("expected compound, got {other:?}"),
445 };
446 assert_eq!(compound.get("maybe"), Some(&Tag::Int(42)));
447
448 let decoded: OptionalField = from_tag(&Tag::Compound(compound)).unwrap();
449 assert_eq!(decoded, OptionalField { maybe: Some(42) });
450 }
451
452 #[test]
453 fn vec_u8_non_empty_encodes_as_byte_array() {
454 let tag = to_tag(&vec![1u8, 2, 3, 250]).unwrap();
455 assert_eq!(tag, Tag::ByteArray(vec![1, 2, 3, 250]));
456 }
457
458 #[test]
459 fn empty_vec_u8_without_wrapper_is_encoded_as_empty_list() {
460 let tag = to_tag(&Vec::<u8>::new()).unwrap();
461 assert_eq!(tag, Tag::List(ListTag::empty(TagType::End)));
462 let contract = std::hint::black_box(SERDE_BEHAVIOR_CONTRACT);
463 assert!(contract.empty_vec_u8_requires_wrapper);
464 }
465
466 #[test]
467 fn nbt_byte_array_wrapper_forces_empty_byte_array_semantics() {
468 #[derive(Serialize, Deserialize, Debug, PartialEq)]
469 struct WrappedBytes {
470 bytes: NbtByteArray,
471 }
472
473 let input = WrappedBytes {
474 bytes: NbtByteArray(Vec::new()),
475 };
476 let tag = to_tag(&input).unwrap();
477 let compound = match tag {
478 Tag::Compound(value) => value,
479 other => panic!("expected compound, got {other:?}"),
480 };
481 assert_eq!(compound.get("bytes"), Some(&Tag::ByteArray(Vec::new())));
482
483 let output: WrappedBytes = from_tag(&Tag::Compound(compound)).unwrap();
484 assert_eq!(output, input);
485 }
486
487 #[test]
488 fn byte_array_helper_roundtrip() {
489 let tag = to_byte_array_tag(vec![9u8, 8, 7]);
490 let bytes = from_byte_array_tag(&tag).unwrap();
491 assert_eq!(bytes, vec![9, 8, 7]);
492
493 let wrong = Tag::Int(1);
494 let err = from_byte_array_tag(&wrong).unwrap_err();
495 assert!(matches!(
496 err,
497 Error::UnexpectedType {
498 context: "byte_array_tag_decode",
499 expected_id,
500 actual_id
501 } if expected_id == TagType::ByteArray.id() && actual_id == TagType::Int.id()
502 ));
503 }
504
505 #[test]
506 fn contract_flags_are_expected() {
507 let contract = std::hint::black_box(SERDE_BEHAVIOR_CONTRACT);
508 assert!(contract.option_some_as_inner_tag);
509 assert!(contract.option_none_rejected);
510 assert!(contract.vec_u8_non_empty_as_byte_array);
511 assert!(contract.empty_vec_u8_requires_wrapper);
512 }
513
514 #[test]
515 fn byte_array_tag_decodes_to_vec_u8() {
516 #[derive(Deserialize, Debug, PartialEq)]
517 struct ByteVecHolder {
518 bytes: Vec<u8>,
519 }
520
521 let mut compound = IndexMap::new();
522 compound.insert("bytes".to_string(), Tag::ByteArray(vec![4, 5, 6]));
523 let decoded: ByteVecHolder = from_tag(&Tag::Compound(compound)).unwrap();
524 assert_eq!(
525 decoded,
526 ByteVecHolder {
527 bytes: vec![4, 5, 6]
528 }
529 );
530 }
531}