#[cfg(test)]
mod test;
use std::{
fmt::{self, Display},
str::FromStr,
};
use crate::{
error::{Error, Result},
spec::BinarySubtype,
Binary,
Bson,
};
#[cfg(feature = "serde")]
pub(crate) const UUID_NEWTYPE_NAME: &str = "$__bson_private_uuid";
#[derive(Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
pub struct Uuid {
uuid: uuid::Uuid,
}
impl Uuid {
pub fn new() -> Self {
Self {
uuid: uuid::Uuid::new_v4(),
}
}
pub const fn from_bytes(bytes: [u8; 16]) -> Self {
Self::from_external_uuid(uuid::Uuid::from_bytes(bytes))
}
pub fn parse_str(input: impl AsRef<str>) -> Result<Self> {
let uuid = uuid::Uuid::parse_str(input.as_ref()).map_err(Error::invalid_uuid_string)?;
Ok(Self::from_external_uuid(uuid))
}
pub(crate) const fn from_external_uuid(uuid: uuid::Uuid) -> Self {
Self { uuid }
}
pub const fn bytes(self) -> [u8; 16] {
*self.uuid.as_bytes()
}
}
impl Default for Uuid {
fn default() -> Self {
Self::new()
}
}
impl FromStr for Uuid {
type Err = Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Self::parse_str(s)
}
}
#[cfg(feature = "uuid-1")]
impl Uuid {
pub fn from_uuid_1(uuid: uuid::Uuid) -> Self {
Self::from_external_uuid(uuid)
}
pub fn to_uuid_1(self) -> uuid::Uuid {
self.uuid
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Uuid {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_newtype_struct(UUID_NEWTYPE_NAME, &self.uuid)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Uuid {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
match deserializer.deserialize_newtype_struct(UUID_NEWTYPE_NAME, crate::de::BsonVisitor)? {
Bson::Binary(b)
if matches!(b.subtype, BinarySubtype::Uuid | BinarySubtype::Generic) =>
{
let uuid =
uuid::Uuid::from_slice(b.bytes.as_slice()).map_err(serde::de::Error::custom)?;
Ok(Self::from_external_uuid(uuid))
}
Bson::Binary(b) if b.subtype == BinarySubtype::UuidOld => {
Err(serde::de::Error::custom(
"received legacy UUID (subtype 3) but expected regular UUID (subtype 4)",
))
}
Bson::String(s) => {
use std::str::FromStr as _;
let uuid = uuid::Uuid::from_str(s.as_str()).map_err(serde::de::Error::custom)?;
Ok(Self::from_external_uuid(uuid))
}
b => Err(serde::de::Error::invalid_type(b.as_unexpected(), &"a UUID")),
}
}
}
impl Display for Uuid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.uuid.fmt(f)
}
}
impl std::fmt::Debug for Uuid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
std::fmt::Debug::fmt(&self.uuid, f)
}
}
impl From<Uuid> for Binary {
fn from(uuid: Uuid) -> Self {
Binary {
subtype: BinarySubtype::Uuid,
bytes: uuid.bytes().to_vec(),
}
}
}
impl From<Uuid> for Bson {
fn from(u: Uuid) -> Self {
Bson::Binary(u.into())
}
}
#[cfg(feature = "uuid-1")]
impl From<uuid::Uuid> for Uuid {
fn from(u: uuid::Uuid) -> Self {
Self::from_uuid_1(u)
}
}
#[cfg(feature = "uuid-1")]
impl From<Uuid> for uuid::Uuid {
fn from(s: Uuid) -> Self {
s.to_uuid_1()
}
}
#[non_exhaustive]
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum UuidRepresentation {
Standard,
CSharpLegacy,
JavaLegacy,
PythonLegacy,
}
impl Binary {
pub fn from_uuid(uuid: Uuid) -> Self {
Binary::from(uuid)
}
pub fn from_uuid_with_representation(uuid: Uuid, rep: UuidRepresentation) -> Self {
match rep {
UuidRepresentation::Standard => Binary::from_uuid(uuid),
UuidRepresentation::CSharpLegacy => {
let mut bytes = uuid.bytes().to_vec();
bytes[0..4].reverse();
bytes[4..6].reverse();
bytes[6..8].reverse();
Binary {
subtype: BinarySubtype::UuidOld,
bytes,
}
}
UuidRepresentation::PythonLegacy => Binary {
subtype: BinarySubtype::UuidOld,
bytes: uuid.bytes().to_vec(),
},
UuidRepresentation::JavaLegacy => {
let mut bytes = uuid.bytes().to_vec();
bytes[0..8].reverse();
bytes[8..16].reverse();
Binary {
subtype: BinarySubtype::UuidOld,
bytes,
}
}
}
}
pub fn to_uuid_with_representation(&self, rep: UuidRepresentation) -> Result<Uuid> {
if rep != UuidRepresentation::Standard && self.subtype != BinarySubtype::UuidOld {
return Err(Error::uuid_representation_mismatch(
rep,
self.subtype,
BinarySubtype::UuidOld,
));
}
if rep == UuidRepresentation::Standard && self.subtype != BinarySubtype::Uuid {
return Err(Error::uuid_representation_mismatch(
rep,
self.subtype,
BinarySubtype::UuidOld,
));
}
if self.bytes.len() != 16 {
return Err(Error::invalid_uuid_length(self.bytes.len()));
}
let mut buf = [0u8; 16];
buf.copy_from_slice(&self.bytes);
Ok(match rep {
UuidRepresentation::Standard => Uuid::from_bytes(buf),
UuidRepresentation::CSharpLegacy => {
buf[0..4].reverse();
buf[4..6].reverse();
buf[6..8].reverse();
Uuid::from_bytes(buf)
}
UuidRepresentation::PythonLegacy => Uuid::from_bytes(buf),
UuidRepresentation::JavaLegacy => {
buf[0..8].reverse();
buf[8..16].reverse();
Uuid::from_bytes(buf)
}
})
}
pub fn to_uuid(&self) -> Result<Uuid> {
self.to_uuid_with_representation(UuidRepresentation::Standard)
}
}
#[cfg(feature = "uuid-1")]
impl From<uuid::Uuid> for Binary {
fn from(uuid: uuid::Uuid) -> Self {
Binary {
subtype: BinarySubtype::Uuid,
bytes: uuid.as_bytes().to_vec(),
}
}
}