1
0
Fork 0
mirror of https://github.com/azalea-rs/simdnbt.git synced 2025-08-02 15:36:03 +00:00

Serialize macro

This commit is contained in:
mat 2023-11-19 00:14:04 -06:00
parent 7d4b7b9a22
commit 331d135910
8 changed files with 239 additions and 26 deletions

View file

@ -51,3 +51,51 @@ pub fn deserialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenSt
output.into()
}
#[proc_macro_derive(Serialize, attributes(simdnbt))]
pub fn serialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let ident = input.ident;
let mut field_serializers = Vec::<proc_macro2::TokenStream>::new();
match input.data {
syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields {
syn::Fields::Named(syn::FieldsNamed { named, .. }) => {
for field in named {
let struct_field_name = field.ident.unwrap();
let mut field_attrs = parse_field_attrs(&field.attrs);
let field_name = field_attrs
.rename
.take()
.unwrap_or_else(|| struct_field_name.to_string());
field_serializers.push(quote! {
if let Some(item) = simdnbt::ToNbtTag::to_optional_nbt_tag(self.#struct_field_name) {
nbt.insert(#field_name, item);
}
});
}
}
syn::Fields::Unnamed(_) => todo!(),
syn::Fields::Unit => todo!(),
},
syn::Data::Enum(_) => todo!(),
syn::Data::Union(_) => todo!(),
}
let output = quote! {
impl simdnbt::Serialize for #ident {
fn to_compound(self) -> simdnbt::owned::NbtCompound {
let mut nbt = simdnbt::owned::NbtCompound::new();
#(#field_serializers)*
nbt
}
}
};
output.into()
}

View file

@ -1,8 +1,8 @@
use std::{collections::HashMap, hint::black_box, io::Cursor};
use simdnbt::{owned::Nbt, Deserialize};
use simdnbt::{owned::Nbt, Deserialize, Serialize};
#[derive(Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Item {
pub id: i16,
#[simdnbt(rename = "Damage")]
@ -13,7 +13,7 @@ pub struct Item {
pub tag: ItemTag,
}
#[derive(Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ItemTag {
#[simdnbt(rename = "SkullOwner")]
pub skull_owner: Option<SkullOwner>,
@ -22,7 +22,7 @@ pub struct ItemTag {
pub display: ItemDisplay,
}
#[derive(Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ExtraAttributes {
pub id: Option<String>,
pub modifier: Option<String>,
@ -32,23 +32,23 @@ pub struct ExtraAttributes {
pub timestamp: Option<String>,
}
#[derive(Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct SkullOwner {
pub properties: Properties,
}
#[derive(Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Properties {
pub textures: Vec<Texture>,
}
#[derive(Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Texture {
#[simdnbt(rename = "Value")]
pub value: String,
}
#[derive(Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ItemDisplay {
#[simdnbt(rename = "Name")]
pub name: String,
@ -58,7 +58,7 @@ pub struct ItemDisplay {
pub color: Option<i32>,
}
#[derive(Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Base {
#[simdnbt(rename = "i")]
pub items: Vec<Option<Item>>,
@ -71,8 +71,12 @@ fn main() {
let nbt = Nbt::read(&mut Cursor::new(input));
let nbt = black_box(nbt.unwrap().unwrap());
let data = Base::from_nbt(nbt).unwrap().items;
let data = Base::from_nbt(nbt).unwrap();
println!("data: {data:?}");
// roundtrip
let new_data = Base::from_nbt(data.clone().to_nbt()).unwrap();
assert_eq!(data, new_data);
println!("data: {:?}", data.items);
}
}

View file

@ -1,6 +1,10 @@
use simdnbt::ToNbtTag;
use std::{collections::HashMap, hint::black_box, io::Cursor};
use simdnbt::borrow::{BaseNbt, Nbt};
use simdnbt::{
borrow::{BaseNbt, Nbt},
owned,
};
#[derive(Clone, PartialEq, Debug)]
pub struct Item {
@ -29,7 +33,7 @@ pub struct ItemDisplay {
pub color: Option<i32>,
}
fn simdnbt_items_from_nbt(nbt: BaseNbt) -> Option<Vec<Option<Item>>> {
fn items_from_nbt(nbt: BaseNbt) -> Option<Vec<Option<Item>>> {
let mut items = Vec::new();
for item_nbt in nbt
.list("i")
@ -99,13 +103,44 @@ fn simdnbt_items_from_nbt(nbt: BaseNbt) -> Option<Vec<Option<Item>>> {
Some(items)
}
fn nbt_from_items(items: Vec<Option<Item>>) -> owned::BaseNbt {
owned::BaseNbt::new("", {
let i = {
let mut i = Vec::new();
for item in items {
let Some(item) = item else {
i.push(owned::NbtCompound::new());
continue;
};
let mut nbt = {
let mut nbt = owned::NbtCompound::new();
nbt.insert("id", item.id.to_nbt_tag());
nbt.insert("Damage", item.damage.to_nbt_tag());
nbt.insert("Count", item.count.to_nbt_tag());
nbt
};
i.push(nbt);
}
owned::NbtList::Compound(i)
};
let mut nbt = owned::NbtCompound::new();
nbt.insert("i", owned::NbtTag::List(i));
nbt
})
}
fn main() {
let input = black_box(include_bytes!("../tests/realworld.nbt"));
for _ in 0..1 {
let nbt = Nbt::read(&mut Cursor::new(input));
let nbt = black_box(nbt.unwrap().unwrap());
println!("{:?}", simdnbt_items_from_nbt(nbt));
println!("{:?}", items_from_nbt(nbt));
// black_box(simdnbt_items_from_nbt(nbt));
}
}

View file

@ -26,6 +26,6 @@ mod traits;
pub use error::{DeserializeError, Error};
pub use mutf8::Mutf8Str;
pub use traits::{Deserialize, FromNbtTag};
pub use traits::{Deserialize, FromNbtTag, Serialize, ToNbtTag};
pub use simdnbt_derive::*;

View file

@ -194,6 +194,12 @@ impl From<String> for Mutf8String {
Self::from_string(s)
}
}
impl From<&str> for Mutf8String {
#[inline]
fn from(s: &str) -> Self {
Self::from_string(s.to_owned())
}
}
#[cfg(test)]
mod tests {

View file

@ -26,6 +26,10 @@ impl NbtCompound {
Self::default()
}
pub fn from_values(values: Vec<(Mutf8String, NbtTag)>) -> Self {
Self { values }
}
pub fn read(data: &mut Cursor<&[u8]>) -> Result<Self, Error> {
Self::read_with_depth(data, 0)
}

View file

@ -115,7 +115,8 @@ impl Deref for Nbt {
}
impl BaseNbt {
pub fn new(name: Mutf8String, tag: NbtCompound) -> Self {
pub fn new(name: impl Into<Mutf8String>, tag: NbtCompound) -> Self {
let name = name.into();
Self { name, tag }
}

View file

@ -10,6 +10,33 @@ pub trait Deserialize: Sized {
fn from_compound(compound: crate::owned::NbtCompound) -> Result<Self, DeserializeError>;
}
pub trait Serialize: Sized {
fn to_nbt(self) -> crate::owned::BaseNbt {
crate::owned::BaseNbt::new("", self.to_compound())
}
fn to_compound(self) -> crate::owned::NbtCompound;
}
pub trait FromNbtTag: Sized {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self>;
fn from_optional_nbt_tag(
tag: Option<crate::owned::NbtTag>,
) -> Result<Option<Self>, DeserializeError> {
match tag {
Some(tag) => Ok(Self::from_nbt_tag(tag)),
None => Err(DeserializeError::MissingField),
}
}
}
pub trait ToNbtTag: Sized {
fn to_nbt_tag(self) -> crate::owned::NbtTag;
fn to_optional_nbt_tag(self) -> Option<crate::owned::NbtTag> {
Some(self.to_nbt_tag())
}
}
impl<T: FromNbtTag> Deserialize for HashMap<String, T> {
fn from_compound(compound: crate::owned::NbtCompound) -> Result<Self, DeserializeError> {
let mut hashmap = HashMap::with_capacity(compound.values.len());
@ -24,22 +51,26 @@ impl<T: FromNbtTag> Deserialize for HashMap<String, T> {
Ok(hashmap)
}
}
impl<T: ToNbtTag> Serialize for HashMap<String, T> {
fn to_compound(self) -> crate::owned::NbtCompound {
let mut compound = crate::owned::NbtCompound::new();
for (k, v) in self {
compound.insert(k, v.to_nbt_tag());
}
compound
}
}
impl Deserialize for crate::owned::NbtCompound {
fn from_compound(compound: crate::owned::NbtCompound) -> Result<Self, DeserializeError> {
Ok(compound)
}
}
pub trait FromNbtTag: Sized {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self>;
fn from_optional_nbt_tag(
tag: Option<crate::owned::NbtTag>,
) -> Result<Option<Self>, DeserializeError> {
match tag {
Some(tag) => Ok(Self::from_nbt_tag(tag)),
None => Err(DeserializeError::MissingField),
}
impl Serialize for crate::owned::NbtCompound {
fn to_compound(self) -> crate::owned::NbtCompound {
self
}
}
@ -50,42 +81,89 @@ impl<T: Deserialize> FromNbtTag for T {
}
}
impl<T: Serialize> ToNbtTag for T {
fn to_nbt_tag(self) -> crate::owned::NbtTag {
crate::owned::NbtTag::Compound(self.to_compound())
}
}
// standard nbt types
impl FromNbtTag for i8 {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
tag.byte()
}
}
impl ToNbtTag for i8 {
fn to_nbt_tag(self) -> crate::owned::NbtTag {
crate::owned::NbtTag::Byte(self)
}
}
impl FromNbtTag for i16 {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
tag.short()
}
}
impl ToNbtTag for i16 {
fn to_nbt_tag(self) -> crate::owned::NbtTag {
crate::owned::NbtTag::Short(self)
}
}
impl FromNbtTag for i32 {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
tag.int()
}
}
impl ToNbtTag for i32 {
fn to_nbt_tag(self) -> crate::owned::NbtTag {
crate::owned::NbtTag::Int(self)
}
}
impl FromNbtTag for i64 {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
tag.long()
}
}
impl ToNbtTag for i64 {
fn to_nbt_tag(self) -> crate::owned::NbtTag {
crate::owned::NbtTag::Long(self)
}
}
impl FromNbtTag for f32 {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
tag.float()
}
}
impl ToNbtTag for f32 {
fn to_nbt_tag(self) -> crate::owned::NbtTag {
crate::owned::NbtTag::Float(self)
}
}
impl FromNbtTag for f64 {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
tag.double()
}
}
impl ToNbtTag for f64 {
fn to_nbt_tag(self) -> crate::owned::NbtTag {
crate::owned::NbtTag::Double(self)
}
}
impl FromNbtTag for String {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
tag.string().map(|s| s.to_string())
}
}
impl ToNbtTag for String {
fn to_nbt_tag(self) -> crate::owned::NbtTag {
crate::owned::NbtTag::String(self.into())
}
}
// lists
impl FromNbtTag for Vec<String> {
@ -96,6 +174,13 @@ impl FromNbtTag for Vec<String> {
})
}
}
impl ToNbtTag for Vec<String> {
fn to_nbt_tag(self) -> crate::owned::NbtTag {
crate::owned::NbtTag::List(crate::owned::NbtList::String(
self.into_iter().map(|s| s.into()).collect(),
))
}
}
// slightly less standard types
impl<T: FromNbtTag> FromNbtTag for Option<T> {
@ -111,6 +196,17 @@ impl<T: FromNbtTag> FromNbtTag for Option<T> {
}
}
}
impl<T: ToNbtTag> ToNbtTag for Option<T> {
fn to_nbt_tag(self) -> crate::owned::NbtTag {
panic!("Called to_nbt_tag on Option<T>. Use to_optional_nbt_tag instead.")
}
fn to_optional_nbt_tag(self) -> Option<crate::owned::NbtTag> {
match self {
Some(t) => Some(t.to_nbt_tag()),
None => None,
}
}
}
impl<T: Deserialize> FromNbtTag for Vec<Option<T>> {
/// A list of compounds where `None` is an empty compound
@ -128,6 +224,18 @@ impl<T: Deserialize> FromNbtTag for Vec<Option<T>> {
Some(vec)
}
}
impl<T: Serialize> ToNbtTag for Vec<Option<T>> {
fn to_nbt_tag(self) -> crate::owned::NbtTag {
crate::owned::NbtTag::List(crate::owned::NbtList::Compound(
self.into_iter()
.map(|t| match t {
Some(t) => t.to_compound(),
None => crate::owned::NbtCompound::new(),
})
.collect(),
))
}
}
impl<T: Deserialize> FromNbtTag for Vec<T> {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
@ -140,6 +248,13 @@ impl<T: Deserialize> FromNbtTag for Vec<T> {
Some(vec)
}
}
impl<T: Serialize> ToNbtTag for Vec<T> {
fn to_nbt_tag(self) -> crate::owned::NbtTag {
crate::owned::NbtTag::List(crate::owned::NbtList::Compound(
self.into_iter().map(|t| t.to_compound()).collect(),
))
}
}
impl FromNbtTag for bool {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {