From 50c11364fe9f96c5412a7781b7e1fb0f0726bf3f Mon Sep 17 00:00:00 2001 From: Esteban Borai Date: Sun, 8 Sep 2024 21:21:01 -0300 Subject: [PATCH] fix(serde): serialization should use `str` over `[u8]` (#21) Currently `Pxid` deserialize expects a `[u8]` which is not right because JSON payloads, for instance, represents `Pxid` as a `String` instead. --- Cargo.lock | 1 + Cargo.toml | 1 + src/id.rs | 89 +++++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 66 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 733cde7..e78afff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -639,6 +639,7 @@ dependencies = [ "md5", "rand", "serde", + "serde_json", "serde_test", "sysctl", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index d1f5c2f..ad02f58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ serde = { version = "1.0.193", features = ["derive"], optional = true } [dev-dependencies] serde_test = "1.0.176" +serde_json = "1.0.68" [target.'cfg(target_os = "macos")'.dependencies] sysctl = "0.5.5" diff --git a/src/id.rs b/src/id.rs index a96e3d5..c11037d 100644 --- a/src/id.rs +++ b/src/id.rs @@ -14,7 +14,10 @@ use async_graphql::connection::CursorType; use async_graphql::{InputValueError, InputValueResult, Scalar, ScalarType, Value}; #[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[cfg(feature = "serde")] +use serde::de::Visitor; use crate::error::{DecodeError, Error}; use crate::host_id::{machine_id, MachineIdBytes}; @@ -104,7 +107,6 @@ pub type Bytes = [u8; BINARY_LENGTH]; /// ``` /// #[derive(Clone, Copy, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub struct Pxid(pub(crate) Bytes); impl Pxid { @@ -566,6 +568,45 @@ impl CursorType for Pxid { } } +#[cfg(feature = "serde")] +struct PxidVisitor; + +#[cfg(feature = "serde")] +impl<'de> Visitor<'de> for PxidVisitor { + type Value = Pxid; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a valid Pxid string") + } + + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + Pxid::from_str(value).map_err(serde::de::Error::custom) + } +} + +#[cfg(feature = "serde")] +impl Serialize for Pxid { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for Pxid { + fn deserialize(deserializer: D) -> std::result::Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(PxidVisitor) + } +} + #[cfg(feature = "async-graphql")] #[cfg(test)] mod asyng_graphql_tests { @@ -817,29 +858,27 @@ mod tests { fn pxid_serialization() { let pxid = Pxid::from_str("acct_9m4e2mr0ui3e8a215n4g").unwrap(); - assert_ser_tokens( - &pxid.compact(), - &[ - Token::NewtypeStruct { name: "Pxid" }, - Token::Tuple { len: 16 }, - Token::U8(97), - Token::U8(99), - Token::U8(99), - Token::U8(116), - Token::U8(77), - Token::U8(136), - Token::U8(225), - Token::U8(91), - Token::U8(96), - Token::U8(244), - Token::U8(134), - Token::U8(228), - Token::U8(40), - Token::U8(65), - Token::U8(45), - Token::U8(201), - Token::TupleEnd, - ], + assert_ser_tokens(&pxid.compact(), &[Token::Str("acct_9m4e2mr0ui3e8a215n4g")]); + } + + #[test] + #[cfg(feature = "serde")] + fn pxid_deserialization() { + #[derive(Debug, Deserialize)] + struct User { + id: Pxid, + } + + let user: User = serde_json::from_str( + r#"{ + "id": "user_crdqga007gvfk4a3t0t0" + }"#, + ) + .expect("Failed to create sample JSON for User"); + + assert_eq!( + user.id, + Pxid::from_str("user_crdqga007gvfk4a3t0t0").unwrap() ); } }