//! A generic UUID-based wrapper, via a newtype pattern //! with a few key integrations used throughout the library. use std::{fmt, hash::Hash, marker::PhantomData, str}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use uuid::Uuid; /// A generic UUID-based newtype. /// /// `Default` is implemented to generate a new UUID /// via [`Uuid::new_v4`]. pub struct Id(Uuid, PhantomData); impl Hash for Id { fn hash(&self, state: &mut H) { self.0.hash(state); } } impl PartialOrd for Id { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for Id { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.0.cmp(&other.0) } } impl Id { #[must_use] pub fn generate() -> Self { Id(Uuid::new_v4(), PhantomData) } } impl Default for Id { fn default() -> Self { Self::generate() } } impl rusqlite::types::FromSql for Id { fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult { Uuid::parse_str(value.as_str()?) .map(Into::into) .map_err(|error| rusqlite::types::FromSqlError::Other(Box::new(error))) } } impl rusqlite::ToSql for Id { fn to_sql(&self) -> rusqlite::Result> { Ok(rusqlite::types::ToSqlOutput::from(self.0.to_string())) } } impl PartialEq for Id { fn eq(&self, other: &Self) -> bool { self.0.eq(&other.0) } } impl Eq for Id {} impl From for Id { fn from(value: Uuid) -> Self { Self(value, PhantomData) } } impl<'de, T> Deserialize<'de> for Id { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { Uuid::deserialize(deserializer).map(Into::into) } } impl Serialize for Id { fn serialize(&self, serializer: S) -> Result where S: Serializer, { self.0.serialize(serializer) } } impl Clone for Id { fn clone(&self) -> Self { *self } } impl fmt::Display for Id { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } impl fmt::Debug for Id { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } impl Copy for Id {} impl str::FromStr for Id { type Err = uuid::Error; fn from_str(s: &str) -> Result { Uuid::parse_str(s).map(Into::into) } }