Skip to content

Commit

Permalink
SERDE HELL
Browse files Browse the repository at this point in the history
  • Loading branch information
JieningYu committed Feb 7, 2024
1 parent 081da24 commit 40a3365
Show file tree
Hide file tree
Showing 6 changed files with 494 additions and 90 deletions.
2 changes: 2 additions & 0 deletions core/item/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ rimecraft-fmt = { path = "../../util/fmt" }
rimecraft-freezer = { path = "../../util/freezer" }
rimecraft-attachment = { path = "../../util/attachment" }
rimecraft-serde-update = { path = "../../util/serde-update", optional = true }
rimecraft-serde-humanreadctl = { path = "../../util/serde-humanreadctl", optional = true }
fastnbt = "2.4"
rimecraft-nbt-ext = { path = "../../util/nbt-ext" }
serde = { version = "1.0", optional = true, features = ["derive"] }
Expand All @@ -28,6 +29,7 @@ serde = [
"rimecraft-registry/serde",
"rimecraft-attachment/serde",
"dep:rimecraft-serde-update",
"dep:rimecraft-serde-humanreadctl",
]

[lints]
Expand Down
2 changes: 1 addition & 1 deletion core/item/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::{marker::PhantomData, num::NonZeroU32};
use rimecraft_fmt::Formatting;
use rimecraft_registry::{ProvideRegistry, Reg};

mod stack;
pub mod stack;

pub use stack::ItemStack;

Expand Down
118 changes: 85 additions & 33 deletions core/item/src/stack.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Item stack related types and traits.
use rimecraft_attachment::Attachments;
use rimecraft_nbt_ext::Compound;

Expand All @@ -21,7 +23,11 @@ use crate::{Item, ToItem};
))
)]
pub struct ItemStack<'r, K, P> {
#[cfg_attr(feature = "serde", serde(rename = "id"))]
#[cfg_attr(
feature = "serde",
serde(rename = "id"),
serde(with = "serde_helper::item_serde")
)]
item: Item<'r, K, P>,

#[cfg_attr(feature = "serde", serde(rename = "Count"))]
Expand All @@ -33,45 +39,91 @@ pub struct ItemStack<'r, K, P> {

#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "should_skip_attachment_ser"),
serde(default),
serde(serialize_with = "ser_attachments"),
serde(deserialize_with = "deser_attachments")
serde(skip_serializing_if = "serde_helper::should_skip_attachment_ser"),
serde(default = "serde_helper::default_attachments"),
serde(serialize_with = "serde_helper::ser_attachments"),
serde(deserialize_with = "serde_helper::deser_attachments")
)]
attachments: (Attachments<K>, PhantomData<P>),
}

#[cfg(feature = "serde")]
fn should_skip_attachment_ser<K, P>(attachments: &(Attachments<K>, PhantomData<P>)) -> bool {
attachments.0.is_persistent_data_empty()
}
mod serde_helper {
use super::*;

#[cfg(feature = "serde")]
fn ser_attachments<K, P, S>(
attachments: &(Attachments<K>, PhantomData<P>),
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
K: serde::Serialize + Hash + Eq,
{
serde::Serialize::serialize(&attachments.0, serializer)
}
#[inline]
pub fn default_attachments<K, P>() -> (Attachments<K>, PhantomData<P>)
where
P: InitAttachments<K>,
{
let mut att = Attachments::new();
P::init_attachments(&mut att);
(att, PhantomData)
}

#[cfg(feature = "serde")]
fn deser_attachments<'de, K, P, D>(
deserializer: D,
) -> Result<(Attachments<K>, PhantomData<P>), <D as serde::Deserializer<'de>>::Error>
where
D: serde::Deserializer<'de>,
P: InitAttachments<K>,
K: serde::Deserialize<'de> + rimecraft_serde_update::Update<'de> + Hash + Eq,
{
use rimecraft_serde_update::Update;
let mut attachments = Attachments::new();
P::init_attachments(&mut attachments);
attachments.update(deserializer)?;
Ok((attachments, PhantomData))
#[inline]
pub fn should_skip_attachment_ser<K, P>(
attachments: &(Attachments<K>, PhantomData<P>),
) -> bool {
attachments.0.is_persistent_data_empty()
}

#[inline]
pub fn ser_attachments<K, P, S>(
attachments: &(Attachments<K>, PhantomData<P>),
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
K: serde::Serialize + Hash + Eq,
{
serde::Serialize::serialize(&attachments.0, serializer)
}

pub fn deser_attachments<'de, K, P, D>(
deserializer: D,
) -> Result<(Attachments<K>, PhantomData<P>), <D as serde::Deserializer<'de>>::Error>
where
D: serde::Deserializer<'de>,
P: InitAttachments<K>,
K: serde::Deserialize<'de> + rimecraft_serde_update::Update<'de> + Hash + Eq,
{
use rimecraft_serde_update::Update;
let mut attachments = Attachments::new();
P::init_attachments(&mut attachments);
attachments.update(deserializer)?;
Ok((attachments, PhantomData))
}

pub mod item_serde {
use rimecraft_registry::ProvideRegistry;
use rimecraft_serde_humanreadctl::HumanReadableControlled;
use serde::{Deserialize, Serialize};

use crate::RawItem;

use super::*;

#[inline]
pub fn serialize<K, P, S>(item: &Item<'_, K, P>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
K: Serialize + Hash + Eq,
{
item.serialize(HumanReadableControlled::new(serializer, true))
}

#[inline]
pub fn deserialize<'rr, 'd, K, P, D>(deserializer: D) -> Result<Item<'rr, K, P>, D::Error>
where
'rr: 'd,
D: serde::Deserializer<'d>,
K: Deserialize<'d> + Hash + Eq + std::fmt::Debug + 'rr,
P: InitAttachments<K> + ProvideRegistry<'rr, K, RawItem<P>> + 'rr,
{
Item::deserialize(HumanReadableControlled::new(deserializer, true))
}
}
}

impl<'r, K, P> ItemStack<'r, K, P>
Expand Down
87 changes: 31 additions & 56 deletions util/registry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,19 @@ pub struct Registry<K, T> {
}

/// Reference of a registration.
///
/// # Serialization and Deserialization
///
/// This type can be serialized and deserialized using `serde` and `edcode`.
/// (with `serde` feature and `edcode` feature respectively)
///
/// ## Serde
///
/// When serializing this reference with `serde`, it will serialize the ID
/// of the entry, if the serializer is **human readable**. Otherwise, it will
/// serialize the **raw ID** of the entry.
///
/// This corresponds to the `compressed` option in *Mojang Serialization*.
pub struct Reg<'a, K, T> {
raw: usize,
registry: &'a Registry<K, T>,
Expand Down Expand Up @@ -518,7 +531,7 @@ where
}

#[cfg(feature = "serde")]
pub mod serde {
mod serde {
//! Helper module for `serde` support.
use std::hash::Hash;
Expand All @@ -529,13 +542,16 @@ pub mod serde {
where
K: serde::Serialize,
{
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let entry: &RefEntry<_, _> = self.as_ref();
entry.key.value().serialize(serializer)
if serializer.is_human_readable() {
let entry: &RefEntry<_, _> = self.as_ref();
entry.key.value().serialize(serializer)
} else {
serializer.serialize_i32(self.raw as i32)
}
}
}

Expand All @@ -549,58 +565,17 @@ pub mod serde {
where
D: serde::Deserializer<'de>,
{
let key = K::deserialize(deserializer)?;
T::registry()
.get(&key)
.ok_or_else(|| serde::de::Error::custom(format!("key {key:?} not found")))
}
}

/// Wrapper for serializing a compressed entry.
#[derive(Debug, Clone, Copy)]
pub struct Compressed<T>(pub T);

impl<K, T> serde::Serialize for Compressed<&Reg<'_, K, T>>
where
K: serde::Serialize,
{
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_i32(self.0.raw as i32)
}
}

impl<K, T> serde::Serialize for Compressed<Reg<'_, K, T>>
where
K: serde::Serialize,
{
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
Compressed(&self.0).serialize(serializer)
}
}

impl<'a, 'r, 'de, K, T> serde::Deserialize<'de> for Compressed<Reg<'a, K, T>>
where
'r: 'a,
T: ProvideRegistry<'r, K, T> + 'r,
K: serde::Deserialize<'de> + Hash + Eq + std::fmt::Debug + 'r,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let raw = i32::deserialize(deserializer)? as usize;
T::registry()
.of_raw(raw)
.map(Compressed)
.ok_or_else(|| serde::de::Error::custom(format!("raw id {raw} not found")))
if deserializer.is_human_readable() {
let key = K::deserialize(deserializer)?;
T::registry()
.get(&key)
.ok_or_else(|| serde::de::Error::custom(format!("key {key:?} not found")))
} else {
let raw = i32::deserialize(deserializer)? as usize;
T::registry()
.of_raw(raw)
.ok_or_else(|| serde::de::Error::custom(format!("raw id {raw} not found")))
}
}
}
}
Expand Down
20 changes: 20 additions & 0 deletions util/serde-humanreadctl/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "rimecraft-serde-humanreadctl"
version = "0.1.0"
edition = "2021"
authors = ["JieningYu <[email protected]>"]
description = "Serde human-readable controlling layers"
repository = "https://github.com/rimecraft-rs/rimecraft/"
license = "AGPL-3.0-or-later"
categories = ["rust-patterns"]

[badges]
maintenance = { status = "passively-maintained" }

[dependencies]
serde = "1.0"

[features]

[lints]
workspace = true
Loading

0 comments on commit 40a3365

Please sign in to comment.