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_le_bytes<T: DeserializeOwned>(bytes: &[u8]) -> Result<T> {
130 from_le_bytes_with_config(bytes, &NbtReadConfig::default())
131}
132
133pub fn from_net_bytes<T: DeserializeOwned>(bytes: &[u8]) -> Result<T> {
134 from_net_bytes_with_config(bytes, &NbtReadConfig::default())
135}
136
137pub fn from_be_bytes_with_config<T: DeserializeOwned>(
138 bytes: &[u8],
139 config: &NbtReadConfig,
140) -> Result<T> {
141 let mut cursor = Cursor::new(bytes);
142 let root = read_tag_with_config::<BigEndian, _>(&mut cursor, config)?;
143 ensure_consumed(bytes.len(), cursor.position() as usize)?;
144 from_root_tag(&root)
145}
146
147pub fn from_le_bytes_with_config<T: DeserializeOwned>(
148 bytes: &[u8],
149 config: &NbtReadConfig,
150) -> Result<T> {
151 let mut cursor = Cursor::new(bytes);
152 let root = read_tag_with_config::<LittleEndian, _>(&mut cursor, config)?;
153 ensure_consumed(bytes.len(), cursor.position() as usize)?;
154 from_root_tag(&root)
155}
156
157pub fn from_net_bytes_with_config<T: DeserializeOwned>(
158 bytes: &[u8],
159 config: &NbtReadConfig,
160) -> Result<T> {
161 let mut cursor = Cursor::new(bytes);
162 let root = read_tag_with_config::<NetworkLittleEndian, _>(&mut cursor, config)?;
163 ensure_consumed(bytes.len(), cursor.position() as usize)?;
164 from_root_tag(&root)
165}
166
167fn ensure_consumed(total: usize, consumed: usize) -> Result<()> {
168 if consumed == total {
169 return Ok(());
170 }
171 Err(Error::TrailingPayloadBytes {
172 unread: total - consumed,
173 })
174}
175
176fn serde_error<E: std::fmt::Display>(error: E) -> Error {
177 Error::Serde {
178 message: error.to_string(),
179 }
180}
181
182fn serde_value_to_tag(value: SerdeValue) -> Result<Tag> {
183 match value {
184 SerdeValue::Bool(value) => Ok(Tag::Byte(if value { 1 } else { 0 })),
185 SerdeValue::I8(value) => Ok(Tag::Byte(value)),
186 SerdeValue::I16(value) => Ok(Tag::Short(value)),
187 SerdeValue::I32(value) => Ok(Tag::Int(value)),
188 SerdeValue::I64(value) => Ok(Tag::Long(value)),
189 SerdeValue::U8(value) => Ok(Tag::Short(value as i16)),
190 SerdeValue::U16(value) => i16::try_from(value)
191 .map(Tag::Short)
192 .or_else(|_| Ok(Tag::Int(i32::from(value)))),
193 SerdeValue::U32(value) => {
194 if let Ok(int) = i32::try_from(value) {
195 Ok(Tag::Int(int))
196 } else {
197 Ok(Tag::Long(i64::from(value)))
198 }
199 }
200 SerdeValue::U64(value) => {
201 let long = i64::try_from(value).map_err(|_| serde_error("u64 out of i64 range"))?;
202 Ok(Tag::Long(long))
203 }
204 SerdeValue::F32(value) => Ok(Tag::Float(value)),
205 SerdeValue::F64(value) => Ok(Tag::Double(value)),
206 SerdeValue::Char(value) => Ok(Tag::String(value.to_string())),
207 SerdeValue::String(value) => Ok(Tag::String(value)),
208 SerdeValue::Bytes(bytes) => Ok(Tag::ByteArray(bytes)),
209 SerdeValue::Seq(values) => serde_seq_to_tag(values),
210 SerdeValue::Map(values) => serde_map_to_compound(values).map(Tag::Compound),
211 SerdeValue::Option(None) => Err(serde_error("Option::None is not representable in NBT")),
212 SerdeValue::Option(Some(inner)) => serde_value_to_tag(*inner),
213 SerdeValue::Unit => Err(serde_error("unit values are not representable in NBT")),
214 SerdeValue::Newtype(inner) => serde_value_to_tag(*inner),
215 }
216}
217
218fn serde_seq_to_tag(values: Vec<SerdeValue>) -> Result<Tag> {
219 if values.is_empty() {
220 return Ok(Tag::List(ListTag::empty(TagType::End)));
221 }
222
223 if let Some(bytes) = try_u8_seq_to_byte_array(&values) {
224 return Ok(Tag::ByteArray(bytes));
225 }
226 if let Some(ints) = try_i32_seq_to_int_array(&values) {
227 return Ok(Tag::IntArray(ints));
228 }
229 if let Some(longs) = try_i64_seq_to_long_array(&values) {
230 return Ok(Tag::LongArray(longs));
231 }
232
233 let mut tags = Vec::with_capacity(values.len());
234 for value in values {
235 tags.push(serde_value_to_tag(value)?);
236 }
237 let element_type = tags.first().map(Tag::tag_type).unwrap_or(TagType::End);
238 Ok(Tag::List(ListTag::new(element_type, tags)?))
239}
240
241fn try_u8_seq_to_byte_array(values: &[SerdeValue]) -> Option<Vec<u8>> {
242 let mut out = Vec::with_capacity(values.len());
243 for value in values {
244 match value {
245 SerdeValue::U8(byte) => out.push(*byte),
246 _ => return None,
247 }
248 }
249 Some(out)
250}
251
252fn try_i32_seq_to_int_array(values: &[SerdeValue]) -> Option<Vec<i32>> {
253 let mut out = Vec::with_capacity(values.len());
254 for value in values {
255 match value {
256 SerdeValue::I32(int) => out.push(*int),
257 SerdeValue::U32(int) => out.push(i32::try_from(*int).ok()?),
258 _ => return None,
259 }
260 }
261 Some(out)
262}
263
264fn try_i64_seq_to_long_array(values: &[SerdeValue]) -> Option<Vec<i64>> {
265 let mut out = Vec::with_capacity(values.len());
266 for value in values {
267 match value {
268 SerdeValue::I64(long) => out.push(*long),
269 SerdeValue::U64(long) => out.push(i64::try_from(*long).ok()?),
270 _ => return None,
271 }
272 }
273 Some(out)
274}
275
276fn serde_map_to_compound(values: BTreeMap<SerdeValue, SerdeValue>) -> Result<CompoundTag> {
277 let mut out = CompoundTag::new();
278 for (key, value) in values {
279 let key = serde_key_to_string(key)?;
280 let value = serde_value_to_tag(value)?;
281 out.insert(key, value);
282 }
283 Ok(out)
284}
285
286fn serde_key_to_string(value: SerdeValue) -> Result<String> {
287 match value {
288 SerdeValue::String(value) => Ok(value),
289 SerdeValue::Char(value) => Ok(value.to_string()),
290 SerdeValue::Bool(value) => Ok(value.to_string()),
291 SerdeValue::I8(value) => Ok(value.to_string()),
292 SerdeValue::I16(value) => Ok(value.to_string()),
293 SerdeValue::I32(value) => Ok(value.to_string()),
294 SerdeValue::I64(value) => Ok(value.to_string()),
295 SerdeValue::U8(value) => Ok(value.to_string()),
296 SerdeValue::U16(value) => Ok(value.to_string()),
297 SerdeValue::U32(value) => Ok(value.to_string()),
298 SerdeValue::U64(value) => Ok(value.to_string()),
299 _ => Err(serde_error("map key must be string-like for NBT compound")),
300 }
301}
302
303fn tag_to_serde_value(tag: &Tag) -> Result<SerdeValue> {
304 match tag {
305 Tag::End => Err(serde_error(
306 "TAG_End is not representable as a typed serde value",
307 )),
308 Tag::Byte(value) => Ok(SerdeValue::I8(*value)),
309 Tag::Short(value) => Ok(SerdeValue::I16(*value)),
310 Tag::Int(value) => Ok(SerdeValue::I32(*value)),
311 Tag::Long(value) => Ok(SerdeValue::I64(*value)),
312 Tag::Float(value) => Ok(SerdeValue::F32(*value)),
313 Tag::Double(value) => Ok(SerdeValue::F64(*value)),
314 Tag::ByteArray(values) => Ok(SerdeValue::Seq(
315 values.iter().copied().map(SerdeValue::U8).collect(),
316 )),
317 Tag::String(value) => Ok(SerdeValue::String(value.clone())),
318 Tag::List(list) => {
319 let mut values = Vec::with_capacity(list.elements.len());
320 for element in &list.elements {
321 values.push(tag_to_serde_value(element)?);
322 }
323 Ok(SerdeValue::Seq(values))
324 }
325 Tag::Compound(values) => {
326 let mut map = BTreeMap::new();
327 for (key, value) in values {
328 map.insert(SerdeValue::String(key.clone()), tag_to_serde_value(value)?);
329 }
330 Ok(SerdeValue::Map(map))
331 }
332 Tag::IntArray(values) => Ok(SerdeValue::Seq(
333 values.iter().copied().map(SerdeValue::I32).collect(),
334 )),
335 Tag::LongArray(values) => Ok(SerdeValue::Seq(
336 values.iter().copied().map(SerdeValue::I64).collect(),
337 )),
338 }
339}
340
341#[cfg(test)]
342mod tests {
343 use indexmap::IndexMap;
344 use serde::{Deserialize, Serialize};
345
346 use super::*;
347
348 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
349 struct DemoData {
350 name: String,
351 health: i32,
352 pitch: f32,
353 bytes: Vec<u8>,
354 scores: Vec<i32>,
355 }
356
357 fn sample() -> DemoData {
358 DemoData {
359 name: "Steve".to_string(),
360 health: 20,
361 pitch: 11.5,
362 bytes: vec![1, 2, 3, 250],
363 scores: vec![7, 11, 42],
364 }
365 }
366
367 #[test]
368 fn tag_roundtrip_typed() {
369 let input = sample();
370 let tag = to_tag(&input).unwrap();
371 let output: DemoData = from_tag(&tag).unwrap();
372 assert_eq!(output, input);
373 }
374
375 #[test]
376 fn be_bytes_roundtrip_typed() {
377 let input = sample();
378 let bytes = to_be_bytes(&input).unwrap();
379 let output: DemoData = from_be_bytes(&bytes).unwrap();
380 assert_eq!(output, input);
381 }
382
383 #[test]
384 fn le_bytes_roundtrip_typed() {
385 let input = sample();
386 let bytes = to_le_bytes_named("demo", &input).unwrap();
387 let output: DemoData = from_le_bytes(&bytes).unwrap();
388 assert_eq!(output, input);
389 }
390
391 #[test]
392 fn net_bytes_roundtrip_typed() {
393 let input = sample();
394 let bytes = to_net_bytes(&input).unwrap();
395 let output: DemoData = from_net_bytes(&bytes).unwrap();
396 assert_eq!(output, input);
397 }
398
399 #[test]
400 fn none_option_is_rejected() {
401 #[derive(Serialize)]
402 struct OptionalField {
403 maybe: Option<i32>,
404 }
405
406 let err = to_tag(&OptionalField { maybe: None }).unwrap_err();
407 assert!(matches!(err.innermost(), Error::Serde { .. }));
408 }
409
410 #[test]
411 fn some_option_is_serialized_as_inner_tag() {
412 #[derive(Serialize, Deserialize, Debug, PartialEq)]
413 struct OptionalField {
414 maybe: Option<i32>,
415 }
416
417 let tag = to_tag(&OptionalField { maybe: Some(42) }).unwrap();
418 let compound = match tag {
419 Tag::Compound(value) => value,
420 other => panic!("expected compound, got {other:?}"),
421 };
422 assert_eq!(compound.get("maybe"), Some(&Tag::Int(42)));
423
424 let decoded: OptionalField = from_tag(&Tag::Compound(compound)).unwrap();
425 assert_eq!(decoded, OptionalField { maybe: Some(42) });
426 }
427
428 #[test]
429 fn vec_u8_non_empty_encodes_as_byte_array() {
430 let tag = to_tag(&vec![1u8, 2, 3, 250]).unwrap();
431 assert_eq!(tag, Tag::ByteArray(vec![1, 2, 3, 250]));
432 }
433
434 #[test]
435 fn empty_vec_u8_without_wrapper_is_encoded_as_empty_list() {
436 let tag = to_tag(&Vec::<u8>::new()).unwrap();
437 assert_eq!(tag, Tag::List(ListTag::empty(TagType::End)));
438 let contract = std::hint::black_box(SERDE_BEHAVIOR_CONTRACT);
439 assert!(contract.empty_vec_u8_requires_wrapper);
440 }
441
442 #[test]
443 fn nbt_byte_array_wrapper_forces_empty_byte_array_semantics() {
444 #[derive(Serialize, Deserialize, Debug, PartialEq)]
445 struct WrappedBytes {
446 bytes: NbtByteArray,
447 }
448
449 let input = WrappedBytes {
450 bytes: NbtByteArray(Vec::new()),
451 };
452 let tag = to_tag(&input).unwrap();
453 let compound = match tag {
454 Tag::Compound(value) => value,
455 other => panic!("expected compound, got {other:?}"),
456 };
457 assert_eq!(compound.get("bytes"), Some(&Tag::ByteArray(Vec::new())));
458
459 let output: WrappedBytes = from_tag(&Tag::Compound(compound)).unwrap();
460 assert_eq!(output, input);
461 }
462
463 #[test]
464 fn byte_array_helper_roundtrip() {
465 let tag = to_byte_array_tag(vec![9u8, 8, 7]);
466 let bytes = from_byte_array_tag(&tag).unwrap();
467 assert_eq!(bytes, vec![9, 8, 7]);
468
469 let wrong = Tag::Int(1);
470 let err = from_byte_array_tag(&wrong).unwrap_err();
471 assert!(matches!(
472 err,
473 Error::UnexpectedType {
474 context: "byte_array_tag_decode",
475 expected_id,
476 actual_id
477 } if expected_id == TagType::ByteArray.id() && actual_id == TagType::Int.id()
478 ));
479 }
480
481 #[test]
482 fn contract_flags_are_expected() {
483 let contract = std::hint::black_box(SERDE_BEHAVIOR_CONTRACT);
484 assert!(contract.option_some_as_inner_tag);
485 assert!(contract.option_none_rejected);
486 assert!(contract.vec_u8_non_empty_as_byte_array);
487 assert!(contract.empty_vec_u8_requires_wrapper);
488 }
489
490 #[test]
491 fn byte_array_tag_decodes_to_vec_u8() {
492 #[derive(Deserialize, Debug, PartialEq)]
493 struct ByteVecHolder {
494 bytes: Vec<u8>,
495 }
496
497 let mut compound = IndexMap::new();
498 compound.insert("bytes".to_string(), Tag::ByteArray(vec![4, 5, 6]));
499 let decoded: ByteVecHolder = from_tag(&Tag::Compound(compound)).unwrap();
500 assert_eq!(
501 decoded,
502 ByteVecHolder {
503 bytes: vec![4, 5, 6]
504 }
505 );
506 }
507}