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:
parent
7d4b7b9a22
commit
331d135910
8 changed files with 239 additions and 26 deletions
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
|
||||
|
|
|
@ -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> {
|
||||
|
|
Loading…
Add table
Reference in a new issue