Skip to content

Commit

Permalink
Implement Serde for SharedString (rojo-rbx#414)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dekkonot authored Jun 4, 2024
1 parent 0fa0341 commit 875c0d7
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 22 deletions.
2 changes: 2 additions & 0 deletions rbx_types/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
* Implement `IntoIterator` for `&Attributes`. ([#386])
* Implement `Extend<(String, Variant)>` for `Attributes`. ([#386])
* Implement `clear` and `drain` for `Attributes`. ([#409])
* Implement `Serialize` and `Deserialize` for `SharedString` ([#414])

[#386]: https://github.com/rojo-rbx/rbx-dom/pull/386
[#409]: https://github.com/rojo-rbx/rbx-dom/pull/409
[#414]: https://github.com/rojo-rbx/rbx-dom/pull/414

## 1.8.0 (2024-01-16)
* Add `len` and `is_empty` methods to `Attributes` struct. ([#377])
Expand Down
79 changes: 61 additions & 18 deletions rbx_types/src/shared_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,29 +148,35 @@ impl fmt::Display for SharedStringHash {
}

#[cfg(feature = "serde")]
pub(crate) mod variant_serialization {
pub(crate) mod serde_impl {
use super::*;

use serde::de::Error as _;
use serde::ser::Error as _;
use serde::{Deserializer, Serializer};
use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer};

pub fn serialize<S>(_value: &SharedString, _serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
Err(S::Error::custom(
"SharedString cannot be serialized as part of a Variant",
))
impl Serialize for SharedString {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
if serializer.is_human_readable() {
let encoded = base64::encode(self.data());

serializer.serialize_str(&encoded)
} else {
self.data().serialize(serializer)
}
}
}

pub fn deserialize<'de, D>(_deserializer: D) -> Result<SharedString, D::Error>
where
D: Deserializer<'de>,
{
Err(D::Error::custom(
"SharedString cannot be deserialized as part of a Variant",
))
impl<'de> Deserialize<'de> for SharedString {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
if deserializer.is_human_readable() {
let encoded = <&str>::deserialize(deserializer)?;
let buffer = base64::decode(encoded).map_err(D::Error::custom)?;

Ok(SharedString::new(buffer))
} else {
let buffer = <Vec<u8>>::deserialize(deserializer)?;
Ok(SharedString::new(buffer))
}
}
}
}

Expand Down Expand Up @@ -199,4 +205,41 @@ mod test {
let _y = SharedString::new(vec![5, 6, 7, 1]);
}
}

#[cfg(feature = "serde")]
#[test]
fn serde_human() {
let sstr = SharedString::new(b"a test string".to_vec());
let serialized = serde_json::to_string(&sstr).unwrap();

assert_eq!(serialized, r#""YSB0ZXN0IHN0cmluZw==""#);

let deserialized: SharedString = serde_json::from_str(&serialized).unwrap();

assert_eq!(sstr, deserialized);
}

#[cfg(feature = "serde")]
#[test]
fn serde_non_human() {
use std::{io::Write, mem};

let sstr = SharedString::new(b"a test string".to_vec());
let data = sstr.data();
let serialized = bincode::serialize(&sstr).unwrap();

// Write the length of the string as little-endian u64 followed by the
// bytes of the string. This is analoglous to how bincode does.
let mut expected = Vec::with_capacity(mem::size_of::<u64>() + data.len());
expected
.write_all(&(data.len() as u64).to_le_bytes())
.unwrap();
expected.write_all(data).unwrap();

assert_eq!(serialized, expected);

let deserialized: SharedString = bincode::deserialize(&serialized).unwrap();

assert_eq!(sstr, deserialized);
}
}
4 changes: 0 additions & 4 deletions rbx_types/src/variant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,6 @@ make_variant! {
Ref(Ref),
Region3(Region3),
Region3int16(Region3int16),
#[cfg_attr(
feature = "serde",
serde(with = "crate::shared_string::variant_serialization"),
)]
SharedString(SharedString),
String(String),
UDim(UDim),
Expand Down

0 comments on commit 875c0d7

Please sign in to comment.