Skip to content

Commit

Permalink
feat(serde_with): make Bytes adjustable
Browse files Browse the repository at this point in the history
  • Loading branch information
sivizius committed Oct 10, 2024
1 parent e030aa0 commit 7df47d9
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 31 deletions.
50 changes: 37 additions & 13 deletions serde_with/src/de/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1188,7 +1188,10 @@ where
}
}

impl<'de> DeserializeAs<'de, &'de [u8]> for Bytes {
impl<'de, PREFERENCE> DeserializeAs<'de, &'de [u8]> for Bytes<PREFERENCE>
where
PREFERENCE: formats::TypePreference,
{
fn deserialize_as<D>(deserializer: D) -> Result<&'de [u8], D::Error>
where
D: Deserializer<'de>,
Expand All @@ -1207,7 +1210,10 @@ impl<'de> DeserializeAs<'de, &'de [u8]> for Bytes {
// * visit_str
// * visit_string
#[cfg(feature = "alloc")]
impl<'de> DeserializeAs<'de, Vec<u8>> for Bytes {
impl<'de, PREFERENCE> DeserializeAs<'de, Vec<u8>> for Bytes<PREFERENCE>
where
PREFERENCE: formats::TypePreference,
{
fn deserialize_as<D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'de>,
Expand Down Expand Up @@ -1262,12 +1268,15 @@ impl<'de> DeserializeAs<'de, Vec<u8>> for Bytes {
}

#[cfg(feature = "alloc")]
impl<'de> DeserializeAs<'de, Box<[u8]>> for Bytes {
impl<'de, PREFERENCE> DeserializeAs<'de, Box<[u8]>> for Bytes<PREFERENCE>
where
PREFERENCE: formats::TypePreference,
{
fn deserialize_as<D>(deserializer: D) -> Result<Box<[u8]>, D::Error>
where
D: Deserializer<'de>,
{
<Bytes as DeserializeAs<'de, Vec<u8>>>::deserialize_as(deserializer)
<Bytes<PREFERENCE> as DeserializeAs<'de, Vec<u8>>>::deserialize_as(deserializer)
.map(Vec::into_boxed_slice)
}
}
Expand All @@ -1284,7 +1293,10 @@ impl<'de> DeserializeAs<'de, Box<[u8]>> for Bytes {
// * visit_string
// * visit_seq
#[cfg(feature = "alloc")]
impl<'de> DeserializeAs<'de, Cow<'de, [u8]>> for Bytes {
impl<'de, PREFERENCE> DeserializeAs<'de, Cow<'de, [u8]>> for Bytes<PREFERENCE>
where
PREFERENCE: formats::TypePreference,
{
fn deserialize_as<D>(deserializer: D) -> Result<Cow<'de, [u8]>, D::Error>
where
D: Deserializer<'de>,
Expand Down Expand Up @@ -1354,7 +1366,10 @@ impl<'de> DeserializeAs<'de, Cow<'de, [u8]>> for Bytes {
}
}

impl<'de, const N: usize> DeserializeAs<'de, [u8; N]> for Bytes {
impl<'de, const N: usize, PREFERENCE> DeserializeAs<'de, [u8; N]> for Bytes<PREFERENCE>
where
PREFERENCE: formats::TypePreference,
{
fn deserialize_as<D>(deserializer: D) -> Result<[u8; N], D::Error>
where
D: Deserializer<'de>,
Expand Down Expand Up @@ -1397,7 +1412,10 @@ impl<'de, const N: usize> DeserializeAs<'de, [u8; N]> for Bytes {
}
}

impl<'de, const N: usize> DeserializeAs<'de, &'de [u8; N]> for Bytes {
impl<'de, const N: usize, PREFERENCE> DeserializeAs<'de, &'de [u8; N]> for Bytes<PREFERENCE>
where
PREFERENCE: formats::TypePreference,
{
fn deserialize_as<D>(deserializer: D) -> Result<&'de [u8; N], D::Error>
where
D: Deserializer<'de>,
Expand Down Expand Up @@ -1434,7 +1452,10 @@ impl<'de, const N: usize> DeserializeAs<'de, &'de [u8; N]> for Bytes {
}

#[cfg(feature = "alloc")]
impl<'de, const N: usize> DeserializeAs<'de, Cow<'de, [u8; N]>> for Bytes {
impl<'de, const N: usize, PREFERENCE> DeserializeAs<'de, Cow<'de, [u8; N]>> for Bytes<PREFERENCE>
where
PREFERENCE: formats::TypePreference,
{
fn deserialize_as<D>(deserializer: D) -> Result<Cow<'de, [u8; N]>, D::Error>
where
D: Deserializer<'de>,
Expand Down Expand Up @@ -1531,12 +1552,15 @@ impl<'de, const N: usize> DeserializeAs<'de, Cow<'de, [u8; N]>> for Bytes {
}

#[cfg(feature = "alloc")]
impl<'de, const N: usize> DeserializeAs<'de, Box<[u8; N]>> for Bytes {
impl<'de, const N: usize, PREFERENCE> DeserializeAs<'de, Box<[u8; N]>> for Bytes<PREFERENCE>
where
PREFERENCE: formats::TypePreference,
{
fn deserialize_as<D>(deserializer: D) -> Result<Box<[u8; N]>, D::Error>
where
D: Deserializer<'de>,
{
Bytes::deserialize_as(deserializer).map(Box::new)
<Bytes<PREFERENCE>>::deserialize_as(deserializer).map(Box::new)
}
}

Expand Down Expand Up @@ -1807,17 +1831,17 @@ impl<'de> DeserializeAs<'de, Cow<'de, [u8]>> for BorrowCow {
where
D: Deserializer<'de>,
{
Bytes::deserialize_as(deserializer)
<Bytes<formats::PreferBytes>>::deserialize_as(deserializer)
}
}

Check warning on line 1836 in serde_with/src/de/impls.rs

View workflow job for this annotation

GitHub Actions / Rustfmt (stable)

Diff in /home/runner/work/serde_with/serde_with/serde_with/src/de/impls.rs

#[cfg(feature = "alloc")]
impl<'de, const N: usize> DeserializeAs<'de, Cow<'de, [u8; N]>> for BorrowCow {
impl<'de, const N: usize,> DeserializeAs<'de, Cow<'de, [u8; N]>> for BorrowCow {
fn deserialize_as<D>(deserializer: D) -> Result<Cow<'de, [u8; N]>, D::Error>
where
D: Deserializer<'de>,
{
Bytes::deserialize_as(deserializer)
<Bytes<formats::PreferBytes>>::deserialize_as(deserializer)
}
}

Expand Down
17 changes: 15 additions & 2 deletions serde_with/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1638,7 +1638,7 @@ pub struct TimestampNanoSecondsWithFrac<
/// };
/// assert_eq!(test, serde_json::from_value(j).unwrap());
///
/// // and serialization will always be a byte sequence
/// // and serialization will default to a byte sequence
/// # assert_eq!(json!(
/// {
/// "from_bytes": [70,111,111,45,66,97,114],
Expand All @@ -1647,7 +1647,20 @@ pub struct TimestampNanoSecondsWithFrac<
/// # ), serde_json::to_value(&test).unwrap());
/// # }
/// ```
pub struct Bytes;
///
/// Often it is prefered to serialize these bytes as string again.
/// Like [`BytesOrString`], this can be adjusted using its generic type parameter,
/// which can be either [`PreferBytes`] (default), [`PreferAsciiString`] or [`PreferString`].
/// The latter two will try to convert arbitrary bytes to a `&str` first and will fallback to
/// serializing as array of bytes only if these bytes would form an invalid string.
/// `PreferAsciiString` will serialize strings containing non-ASCII characters as array as well.
///
/// [`PreferBytes`]: formats::PreferBytes
/// [`PreferAsciiString`]: formats::PreferString
/// [`PreferString`]: formats::PreferString
pub struct Bytes<PREFERENCE: formats::TypePreference = formats::PreferBytes>(
PhantomData<PREFERENCE>,
);

/// Deserialize one or many elements
///
Expand Down
56 changes: 40 additions & 16 deletions serde_with/src/ser/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -772,80 +772,104 @@ where
}
}

impl SerializeAs<&[u8]> for Bytes {
impl<PREFERENCE> SerializeAs<&[u8]> for Bytes<PREFERENCE>
where
PREFERENCE: formats::TypePreference,
{
fn serialize_as<S>(bytes: &&[u8], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(bytes)
PREFERENCE::serialize_as(*bytes, serializer)
}
}

#[cfg(feature = "alloc")]
impl SerializeAs<Vec<u8>> for Bytes {
impl<PREFERENCE> SerializeAs<Vec<u8>> for Bytes<PREFERENCE>
where
PREFERENCE: formats::TypePreference,
{
fn serialize_as<S>(bytes: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(bytes)
PREFERENCE::serialize_as(bytes.as_slice(), serializer)
}
}

#[cfg(feature = "alloc")]
impl SerializeAs<Box<[u8]>> for Bytes {
impl<PREFERENCE> SerializeAs<Box<[u8]>> for Bytes<PREFERENCE>
where
PREFERENCE: formats::TypePreference,
{
fn serialize_as<S>(bytes: &Box<[u8]>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(bytes)
PREFERENCE::serialize_as(bytes.as_ref(), serializer)
}
}

#[cfg(feature = "alloc")]
impl<'a> SerializeAs<Cow<'a, [u8]>> for Bytes {
impl<'a, PREFERENCE> SerializeAs<Cow<'a, [u8]>> for Bytes<PREFERENCE>
where
PREFERENCE: formats::TypePreference,
{
fn serialize_as<S>(bytes: &Cow<'a, [u8]>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(bytes)
PREFERENCE::serialize_as(bytes.as_ref(), serializer)
}
}

impl<const N: usize> SerializeAs<[u8; N]> for Bytes {
impl<const N: usize, PREFERENCE> SerializeAs<[u8; N]> for Bytes<PREFERENCE>
where
PREFERENCE: formats::TypePreference,
{
fn serialize_as<S>(bytes: &[u8; N], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(bytes)
PREFERENCE::serialize_as(bytes.as_ref(), serializer)
}
}

impl<const N: usize> SerializeAs<&[u8; N]> for Bytes {
impl<const N: usize, PREFERENCE> SerializeAs<&[u8; N]> for Bytes<PREFERENCE>
where
PREFERENCE: formats::TypePreference,
{
fn serialize_as<S>(bytes: &&[u8; N], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(*bytes)
PREFERENCE::serialize_as(bytes.as_ref(), serializer)
}
}

#[cfg(feature = "alloc")]
impl<const N: usize> SerializeAs<Box<[u8; N]>> for Bytes {
impl<const N: usize, PREFERENCE> SerializeAs<Box<[u8; N]>> for Bytes<PREFERENCE>
where
PREFERENCE: formats::TypePreference,
{
fn serialize_as<S>(bytes: &Box<[u8; N]>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(&**bytes)
PREFERENCE::serialize_as(bytes.as_ref(), serializer)
}
}

#[cfg(feature = "alloc")]
impl<'a, const N: usize> SerializeAs<Cow<'a, [u8; N]>> for Bytes {
impl<'a, const N: usize, PREFERENCE> SerializeAs<Cow<'a, [u8; N]>> for Bytes<PREFERENCE>
where
PREFERENCE: formats::TypePreference,
{
fn serialize_as<S>(bytes: &Cow<'a, [u8; N]>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(bytes.as_ref())
PREFERENCE::serialize_as(bytes.as_ref(), serializer)
}
}

Expand Down

0 comments on commit 7df47d9

Please sign in to comment.