Skip to content

Commit

Permalink
Merge pull request #564 from KodrAus/revert/serde-compact
Browse files Browse the repository at this point in the history
Revert default compact serialization
  • Loading branch information
KodrAus committed Nov 16, 2021
2 parents f362d7d + 90ccef6 commit a1df52a
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 47 deletions.
10 changes: 10 additions & 0 deletions shared/error.rs
Expand Up @@ -14,6 +14,9 @@ pub(crate) enum ErrorKind {
///
/// [`Uuid`]: ../struct.Uuid.html
SimpleLength { len: usize },
/// A byte array didn't contain 16 bytes
#[allow(dead_code)]
ByteLength { len: usize },
/// A hyphenated [`Uuid`] didn't contain 5 groups
///
/// [`Uuid`]: ../struct.Uuid.html
Expand All @@ -26,6 +29,9 @@ pub(crate) enum ErrorKind {
len: usize,
index: usize,
},
/// Some other error occurred.
#[allow(dead_code)]
Other,
}

/// A string that is guaranteed to fail to parse to a [`Uuid`].
Expand Down Expand Up @@ -128,6 +134,9 @@ impl fmt::Display for Error {
len
)
}
ErrorKind::ByteLength { len } => {
write!(f, "invalid length: expected 16 bytes, found {}", len)
}
ErrorKind::GroupCount { count } => {
write!(f, "invalid group count: expected 5, found {}", count)
}
Expand All @@ -139,6 +148,7 @@ impl fmt::Display for Error {
group, expected, len
)
}
ErrorKind::Other => write!(f, "failed to parse a UUID"),
}
}
}
Expand Down
8 changes: 3 additions & 5 deletions src/builder.rs
Expand Up @@ -308,7 +308,7 @@ impl Uuid {
/// ```
pub fn from_slice(b: &[u8]) -> Result<Uuid, Error> {
if b.len() != 16 {
return Err(Error(ErrorKind::SimpleLength { len: b.len() * 2 }));
return Err(Error(ErrorKind::ByteLength { len: b.len() }));
}

let mut bytes: Bytes = [0; 16];
Expand Down Expand Up @@ -349,7 +349,7 @@ impl Uuid {
/// ```
pub fn from_slice_le(b: &[u8]) -> Result<Uuid, Error> {
if b.len() != 16 {
return Err(Error(ErrorKind::SimpleLength { len: b.len() * 2 }));
return Err(Error(ErrorKind::ByteLength { len: b.len() }));
}

let mut bytes: Bytes = [0; 16];
Expand Down Expand Up @@ -452,9 +452,7 @@ impl Uuid {
/// ```
pub fn from_bytes_ref(bytes: &Bytes) -> &Uuid {
// SAFETY: `Bytes` and `Uuid` have the same ABI
unsafe {
&*(bytes as *const Bytes as *const Uuid)
}
unsafe { &*(bytes as *const Bytes as *const Uuid) }
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/external/mod.rs
@@ -1,6 +1,6 @@
#[cfg(feature = "arbitrary")]
mod arbitrary_support;
pub(crate) mod arbitrary_support;
#[cfg(feature = "serde")]
mod serde_support;
pub(crate) mod serde_support;
#[cfg(feature = "slog")]
mod slog_support;
pub(crate) mod slog_support;
146 changes: 112 additions & 34 deletions src/external/serde_support.rs
Expand Up @@ -11,9 +11,7 @@

use crate::{
error::*,
fmt::{
Braced, Hyphenated, Simple, Urn,
},
fmt::{Braced, Hyphenated, Simple, Urn},
std::fmt,
Uuid,
};
Expand All @@ -29,11 +27,10 @@ impl Serialize for Uuid {
) -> Result<S::Ok, S::Error> {
if serializer.is_human_readable() {
serializer.serialize_str(
self.hyphenated()
.encode_lower(&mut Uuid::encode_buffer()),
self.hyphenated().encode_lower(&mut Uuid::encode_buffer()),
)
} else {
self.as_bytes().serialize(serializer)
serializer.serialize_bytes(self.as_bytes())
}
}
}
Expand Down Expand Up @@ -139,9 +136,110 @@ impl<'de> Deserialize<'de> for Uuid {

deserializer.deserialize_str(UuidVisitor)
} else {
let bytes: [u8; 16] = Deserialize::deserialize(deserializer)?;
struct UuidBytesVisitor;

Ok(Uuid::from_bytes(bytes))
impl<'vi> de::Visitor<'vi> for UuidBytesVisitor {
type Value = Uuid;

fn expecting(
&self,
formatter: &mut fmt::Formatter<'_>,
) -> fmt::Result {
write!(formatter, "bytes")
}

fn visit_bytes<E: de::Error>(
self,
value: &[u8],
) -> Result<Uuid, E> {
Uuid::from_slice(value).map_err(de_error)
}
}

deserializer.deserialize_bytes(UuidBytesVisitor)
}
}
}

pub mod compact {
//! Serialize a [`Uuid`] as a `[u8; 16]`.
//!
//! [`Uuid`]: ../../struct.Uuid.html

/// Serialize from a [`Uuid`] as a `[u8; 16]`
///
/// [`Uuid`]: ../../struct.Uuid.html
pub fn serialize<S>(
u: &crate::Uuid,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serde::Serialize::serialize(u.as_bytes(), serializer)
}

/// Deserialize a `[u8; 16]` as a [`Uuid`]
///
/// [`Uuid`]: ../../struct.Uuid.html
pub fn deserialize<'de, D>(deserializer: D) -> Result<crate::Uuid, D::Error>
where
D: serde::Deserializer<'de>,
{
let bytes: [u8; 16] = serde::Deserialize::deserialize(deserializer)?;

Ok(crate::Uuid::from_bytes(bytes))
}

#[cfg(test)]
mod tests {
use serde_derive::*;
use serde_test::{self, Configure};

#[test]
fn test_serialize_compact() {
#[derive(Serialize, Debug, Deserialize, PartialEq)]
struct UuidContainer {
#[serde(with = "crate::serde::compact")]
u: crate::Uuid,
}

let uuid_bytes = b"F9168C5E-CEB2-4F";
let container = UuidContainer {
u: crate::Uuid::from_slice(uuid_bytes).unwrap(),
};

// more complex because of the struct wrapping the actual UUID
// serialization
serde_test::assert_tokens(
&container.compact(),
&[
serde_test::Token::Struct {
name: "UuidContainer",
len: 1,
},
serde_test::Token::Str("u"),
serde_test::Token::Tuple { len: 16 },
serde_test::Token::U8(uuid_bytes[0]),
serde_test::Token::U8(uuid_bytes[1]),
serde_test::Token::U8(uuid_bytes[2]),
serde_test::Token::U8(uuid_bytes[3]),
serde_test::Token::U8(uuid_bytes[4]),
serde_test::Token::U8(uuid_bytes[5]),
serde_test::Token::U8(uuid_bytes[6]),
serde_test::Token::U8(uuid_bytes[7]),
serde_test::Token::U8(uuid_bytes[8]),
serde_test::Token::U8(uuid_bytes[9]),
serde_test::Token::U8(uuid_bytes[10]),
serde_test::Token::U8(uuid_bytes[11]),
serde_test::Token::U8(uuid_bytes[12]),
serde_test::Token::U8(uuid_bytes[13]),
serde_test::Token::U8(uuid_bytes[14]),
serde_test::Token::U8(uuid_bytes[15]),
serde_test::Token::TupleEnd,
serde_test::Token::StructEnd,
],
)
}
}
}
Expand Down Expand Up @@ -204,10 +302,7 @@ mod serde_tests {
fn test_serialize_hyphenated() {
let uuid_str = "f9168c5e-ceb2-4faa-b6bf-329bf39fa1e4";
let u = Uuid::parse_str(uuid_str).unwrap();
serde_test::assert_ser_tokens(
&u.hyphenated(),
&[Token::Str(uuid_str)],
);
serde_test::assert_ser_tokens(&u.hyphenated(), &[Token::Str(uuid_str)]);
}

#[test]
Expand All @@ -232,31 +327,14 @@ mod serde_tests {
}

#[test]
fn test_serialize_compact() {
fn test_serialize_non_human_readable() {
let uuid_bytes = b"F9168C5E-CEB2-4F";
let u = Uuid::from_slice(uuid_bytes).unwrap();
serde_test::assert_tokens(
&u.compact(),
&[
serde_test::Token::Tuple { len: 16 },
serde_test::Token::U8(uuid_bytes[0]),
serde_test::Token::U8(uuid_bytes[1]),
serde_test::Token::U8(uuid_bytes[2]),
serde_test::Token::U8(uuid_bytes[3]),
serde_test::Token::U8(uuid_bytes[4]),
serde_test::Token::U8(uuid_bytes[5]),
serde_test::Token::U8(uuid_bytes[6]),
serde_test::Token::U8(uuid_bytes[7]),
serde_test::Token::U8(uuid_bytes[8]),
serde_test::Token::U8(uuid_bytes[9]),
serde_test::Token::U8(uuid_bytes[10]),
serde_test::Token::U8(uuid_bytes[11]),
serde_test::Token::U8(uuid_bytes[12]),
serde_test::Token::U8(uuid_bytes[13]),
serde_test::Token::U8(uuid_bytes[14]),
serde_test::Token::U8(uuid_bytes[15]),
serde_test::Token::TupleEnd,
],
&[serde_test::Token::Bytes(&[
70, 57, 49, 54, 56, 67, 53, 69, 45, 67, 69, 66, 50, 45, 52, 70,
])],
);
}

Expand All @@ -269,7 +347,7 @@ mod serde_tests {

serde_test::assert_de_tokens_error::<Compact<Uuid>>(
&[Token::Bytes(b"hello_world")],
"invalid type: byte array, expected an array of length 16",
"UUID parsing failed: invalid length: expected 16 bytes, found 11",
);
}
}
14 changes: 12 additions & 2 deletions src/lib.rs
Expand Up @@ -293,8 +293,7 @@ pub enum Variant {
/// * [`hyphenated`](#method.hyphenated):
/// `a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8`.
/// * [`urn`](#method.to_urn): `urn:uuid:A1A2A3A4-B1B2-C1C2-D1D2-D3D4D5D6D7D8`.
/// * [`braced`](#method.braced):
/// `{a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8}`.
/// * [`braced`](#method.braced): `{a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8}`.
///
/// The default representation when formatting a UUID with `Display` is
/// hyphenated:
Expand Down Expand Up @@ -745,6 +744,17 @@ impl AsRef<[u8]> for Uuid {
}
}

#[cfg(feature = "serde")]
pub mod serde {
//! Adapters for `serde`.
//!
//! This module contains adapters you can use with [`#[serde(with)]`](https://serde.rs/field-attrs.html#with)
//! to change the way a [`Uuid`](../struct.Uuid.html) is serialized
//! and deserialized.

pub use crate::external::serde_support::compact;
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
29 changes: 26 additions & 3 deletions src/parser.rs
Expand Up @@ -66,12 +66,35 @@ impl Uuid {
.map_err(InvalidUuid::into_err)
}

/// Intended to replace `Uuid::parse_str`
/// Parses a `Uuid` from a string of hexadecimal digits with optional
/// hyphens.
///
/// This function is similar to [`parse_str`], in fact `parse_str` shares
/// the same underlying parser. The difference is that if `try_parse`
/// fails, it won't generate very useful error messages. The `parse_str`
/// function will eventually be deprecated in favor or `try_parse`.
///
/// # Examples
///
/// Parse a hyphenated UUID:
///
/// ```
/// # use uuid::{Uuid, Version, Variant};
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let uuid = Uuid::try_parse("550e8400-e29b-41d4-a716-446655440000")?;
///
/// assert_eq!(Some(Version::Random), uuid.get_version());
/// assert_eq!(Variant::RFC4122, uuid.get_variant());
/// # Ok(())
/// # }
/// ```
///
/// [`parse_str`]: #method.parse_str
#[inline]
pub const fn try_parse(input: &str) -> Result<Uuid, InvalidUuid> {
pub const fn try_parse(input: &str) -> Result<Uuid, Error> {
match imp::try_parse(input) {
Ok(bytes) => Ok(Uuid::from_bytes(bytes)),
Err(e) => Err(e),
Err(_) => Err(Error(ErrorKind::Other)),
}
}
}
Expand Down

0 comments on commit a1df52a

Please sign in to comment.