diff --git a/serde_nbt/Cargo.toml b/serde_nbt/Cargo.toml new file mode 100644 index 0000000..9a063bf --- /dev/null +++ b/serde_nbt/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "serde_nbt" +description = "A Serde library for Minecraft's Named Binary Tag (NBT) format." +documentation = "https://docs.rs/serde_nbt/" +repository = "https://github.com/rj00a/valence/tree/main/serde_nbt" +readme = "README.md" +license = "MIT" +keywords = ["nbt", "minecraft", "serde", "serialization"] +version = "0.1.0" +authors = ["Ryan Johnson "] +edition = "2021" + +[dependencies] +byteorder = "1.4.3" +cesu8 = "1.1.0" +indexmap = { version = "1.9.1", features = ["serde"] } +serde = "1" +smallvec = { version = "1.9.0", features = ["union"] } + +[dev-dependencies] +hematite-nbt = "0.5.2" +serde_json = "1.0.85" +pretty_assertions = "1.2.1" diff --git a/valence_nbt/src/array.rs b/serde_nbt/src/array.rs similarity index 74% rename from valence_nbt/src/array.rs rename to serde_nbt/src/array.rs index eb7e82d..5ac5003 100644 --- a/valence_nbt/src/array.rs +++ b/serde_nbt/src/array.rs @@ -11,6 +11,33 @@ use crate::{ macro_rules! def_mod { ($index:literal, $mod_name:ident, $display_name:literal, $variant_name:ident) => { + /// Provides (de)serialization support for the NBT type + #[doc = concat!(" \"", $display_name, "\".")] + /// + /// This module is intended to be the target of serde's `#[serde(with = + /// "module")]` field attribute. + /// + /// The target field must serialize and deserialize as a seq. + /// + /// # Examples + /// + /// ``` + /// use serde::{Deserialize, Serialize}; + /// use serde_nbt::binary::to_writer; + /// + /// #[derive(Serialize, Deserialize)] + /// struct MyStruct { + /// #[serde(with = "serde_nbt::int_array")] + /// array: Vec, + /// } + /// + /// let s = MyStruct { + /// array: vec![1, 2, 3], + /// }; + /// + /// let mut buf = Vec::new(); + /// to_writer(&mut buf, &s).unwrap(); + /// ``` pub mod $mod_name { use super::*; diff --git a/valence_nbt/src/binary/de.rs b/serde_nbt/src/binary/de.rs similarity index 66% rename from valence_nbt/src/binary/de.rs rename to serde_nbt/src/binary/de.rs index 1e34b4f..2097bba 100644 --- a/valence_nbt/src/binary/de.rs +++ b/serde_nbt/src/binary/de.rs @@ -11,6 +11,10 @@ mod list; mod payload; mod root; +/// Reads uncompressed NBT binary data from the provided reader. +/// +/// The name of the root compound is discarded. If you need access to it, see +/// [`Deserializer`]. pub fn from_reader(reader: R) -> Result where R: Read, diff --git a/valence_nbt/src/binary/de/array.rs b/serde_nbt/src/binary/de/array.rs similarity index 100% rename from valence_nbt/src/binary/de/array.rs rename to serde_nbt/src/binary/de/array.rs diff --git a/valence_nbt/src/binary/de/compound.rs b/serde_nbt/src/binary/de/compound.rs similarity index 100% rename from valence_nbt/src/binary/de/compound.rs rename to serde_nbt/src/binary/de/compound.rs diff --git a/valence_nbt/src/binary/de/list.rs b/serde_nbt/src/binary/de/list.rs similarity index 100% rename from valence_nbt/src/binary/de/list.rs rename to serde_nbt/src/binary/de/list.rs diff --git a/valence_nbt/src/binary/de/payload.rs b/serde_nbt/src/binary/de/payload.rs similarity index 100% rename from valence_nbt/src/binary/de/payload.rs rename to serde_nbt/src/binary/de/payload.rs diff --git a/valence_nbt/src/binary/de/root.rs b/serde_nbt/src/binary/de/root.rs similarity index 84% rename from valence_nbt/src/binary/de/root.rs rename to serde_nbt/src/binary/de/root.rs index 6bbac2e..b296393 100644 --- a/valence_nbt/src/binary/de/root.rs +++ b/serde_nbt/src/binary/de/root.rs @@ -10,14 +10,24 @@ use smallvec::SmallVec; use crate::binary::de::payload::PayloadDeserializer; use crate::{Error, Tag, CESU8_DECODE_ERROR}; +/// A serde [`Deserializer`] for the binary representation of NBT. #[non_exhaustive] pub struct RootDeserializer { + /// The reader to deserialize from. pub reader: R, + /// The name of the root compound that was deserialized. If + /// [`Self::save_root_name`] is false, then deserialization will not + /// update this value. pub root_name: String, + /// Whether or not the root name should be saved to [`Self::root_name`] + /// during deserialization. pub save_root_name: bool, } impl RootDeserializer { + /// Constructs a new deserializer + /// + /// [`Self::root_name`] is set to the empty string. pub fn new(reader: R, save_root_name: bool) -> Self { Self { reader, diff --git a/valence_nbt/src/binary/ser.rs b/serde_nbt/src/binary/ser.rs similarity index 67% rename from valence_nbt/src/binary/ser.rs rename to serde_nbt/src/binary/ser.rs index e96a565..f9a4ea3 100644 --- a/valence_nbt/src/binary/ser.rs +++ b/serde_nbt/src/binary/ser.rs @@ -13,6 +13,14 @@ mod root; mod seq; mod structs; +/// Writes uncompressed NBT binary data to the provided writer. +/// +/// Note that serialization will fail if the provided value does not serialize +/// as a compound (a map or struct). This is because the NBT format requires the +/// root value to be a named compound. +/// +/// The name of the root compound will be `""`. If you want to use a different +/// name, see [`Serializer`]. pub fn to_writer(writer: W, value: &T) -> Result<()> where W: Write, diff --git a/valence_nbt/src/binary/ser/map.rs b/serde_nbt/src/binary/ser/map.rs similarity index 99% rename from valence_nbt/src/binary/ser/map.rs rename to serde_nbt/src/binary/ser/map.rs index 0007a24..ac99dba 100644 --- a/valence_nbt/src/binary/ser/map.rs +++ b/serde_nbt/src/binary/ser/map.rs @@ -130,7 +130,7 @@ impl Serializer for MapEntrySerializer fn serialize_str(self, v: &str) -> Result { self.value .serialize(&mut PayloadSerializer::named(self.writer, v)) - .map_err(|e| e.field(format!("{v}"))) + .map_err(|e| e.field(v)) } fn serialize_bytes(self, _v: &[u8]) -> Result { diff --git a/valence_nbt/src/binary/ser/payload.rs b/serde_nbt/src/binary/ser/payload.rs similarity index 99% rename from valence_nbt/src/binary/ser/payload.rs rename to serde_nbt/src/binary/ser/payload.rs index 85ac29d..74064ea 100644 --- a/valence_nbt/src/binary/ser/payload.rs +++ b/serde_nbt/src/binary/ser/payload.rs @@ -60,7 +60,7 @@ impl<'w, 'n, W: Write + ?Sized> PayloadSerializer<'w, 'n, W> { match &mut self.state { State::Named(name) => { self.writer.write_u8(tag as u8)?; - write_string(&mut *self.writer, *name)?; + write_string(&mut *self.writer, name)?; } State::FirstListElement { len, written_tag } => { self.writer.write_u8(tag as u8)?; diff --git a/valence_nbt/src/binary/ser/root.rs b/serde_nbt/src/binary/ser/root.rs similarity index 95% rename from valence_nbt/src/binary/ser/root.rs rename to serde_nbt/src/binary/ser/root.rs index 721a0a4..ff32e0c 100644 --- a/valence_nbt/src/binary/ser/root.rs +++ b/serde_nbt/src/binary/ser/root.rs @@ -8,13 +8,19 @@ use crate::binary::ser::structs::SerializeStruct; use crate::binary::ser::{write_string, Impossible}; use crate::{Error, Tag}; +/// A serde [`Serializer`] for the binary representation of NBT. #[non_exhaustive] pub struct RootSerializer<'n, W> { + /// The writer to serialize to. pub writer: W, + /// The name of the root compound to serialize. + /// + /// The empty string `""` is acceptable. pub root_name: &'n str, } impl<'n, W: Write> RootSerializer<'n, W> { + /// Constructs a new serializer. pub fn new(writer: W, root_name: &'n str) -> Self { Self { writer, root_name } } diff --git a/valence_nbt/src/binary/ser/seq.rs b/serde_nbt/src/binary/ser/seq.rs similarity index 100% rename from valence_nbt/src/binary/ser/seq.rs rename to serde_nbt/src/binary/ser/seq.rs diff --git a/valence_nbt/src/binary/ser/structs.rs b/serde_nbt/src/binary/ser/structs.rs similarity index 100% rename from valence_nbt/src/binary/ser/structs.rs rename to serde_nbt/src/binary/ser/structs.rs diff --git a/valence_nbt/src/error.rs b/serde_nbt/src/error.rs similarity index 87% rename from valence_nbt/src/error.rs rename to serde_nbt/src/error.rs index 1927a30..e564dd7 100644 --- a/valence_nbt/src/error.rs +++ b/serde_nbt/src/error.rs @@ -5,6 +5,10 @@ use std::{fmt, io}; use serde::{de, ser}; +/// Errors that can occur when serializing or deserializing. +/// +/// The error type maintains a backtrace through the NBT value which caused the +/// error. This is used in the `Display` impl on the error. #[derive(Debug)] pub struct Error { /// Box this to keep the error as small as possible. We don't want to @@ -50,6 +54,10 @@ impl Error { self } + /// Returns an iterator through the nested fields of an NBT compound to the + /// location where the error occurred. + /// + /// The iterator's `Item` is the name of the current field. pub fn trace( &self, ) -> impl DoubleEndedIterator + ExactSizeIterator + FusedIterator + Clone + '_ diff --git a/valence_nbt/src/lib.rs b/serde_nbt/src/lib.rs similarity index 92% rename from valence_nbt/src/lib.rs rename to serde_nbt/src/lib.rs index 71bb461..967e00c 100644 --- a/valence_nbt/src/lib.rs +++ b/serde_nbt/src/lib.rs @@ -1,3 +1,9 @@ +//! A [serde] library for the serialization and deserialization of Minecraft's +//! [Named Binary Tag] (NBT) format. +//! +//! [serde]: https://docs.rs/serde/latest/serde/ +//! [Named Binary Tag]: https://minecraft.fandom.com/wiki/NBT_format + use std::fmt; use std::fmt::{Display, Formatter}; @@ -145,8 +151,10 @@ impl Display for Tag { } } +/// Error message for cesu-8 decoding failures. const CESU8_DECODE_ERROR: &str = "could not convert CESU-8 data to UTF-8"; +/// The name of the enum used to encode arrays. const ARRAY_ENUM_NAME: &str = "__array__"; const BYTE_ARRAY_VARIANT_NAME: &str = "__byte_array__"; diff --git a/valence_nbt/src/tests.rs b/serde_nbt/src/tests.rs similarity index 100% rename from valence_nbt/src/tests.rs rename to serde_nbt/src/tests.rs diff --git a/valence_nbt/src/value.rs b/serde_nbt/src/value.rs similarity index 97% rename from valence_nbt/src/value.rs rename to serde_nbt/src/value.rs index 930315c..8e69e01 100644 --- a/valence_nbt/src/value.rs +++ b/serde_nbt/src/value.rs @@ -7,7 +7,12 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::{byte_array, int_array, long_array, ArrayType}; -/// Represents an arbitrary NBT value. +/// An arbitrary NBT value. +/// +/// Note that `Value`s at the root level other than [`Value::Compound`] will +/// fail to serialize because NBT requires that the root value be a compound. +/// +/// At the root level you may want to use [`Compound`] instead. #[derive(Clone, PartialEq, Debug)] pub enum Value { Byte(i8), @@ -24,6 +29,9 @@ pub enum Value { LongArray(Vec), } +/// An arbitrary NBT compound. +/// +/// This is a convenient type alias for the [`IndexMap`] type. pub type Compound = IndexMap; /// An NBT list value. diff --git a/valence_nbt/Cargo.toml b/valence_nbt/Cargo.toml deleted file mode 100644 index 9f695f9..0000000 --- a/valence_nbt/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "valence_nbt" -version = "0.1.0" -edition = "2021" - -[dependencies] -byteorder = "1.4.3" -cesu8 = "1.1.0" -indexmap = { version = "1.9.1", features = ["serde"] } -serde = "1" -smallvec = { version = "1.9.0", features = ["union", "const_generics"] } - -[dev-dependencies] -hematite-nbt = "0.5" -serde_json = "1.0.85" -pretty_assertions = "1.2.1" -