use std::fmt;
use std::marker::PhantomData;
use uuid::Uuid;
mod private {
pub trait Sealed {}
}
pub trait IdMarker: private::Sealed + Send + Sync + 'static {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SessionMarker;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StreamMarker;
impl private::Sealed for SessionMarker {}
impl private::Sealed for StreamMarker {}
impl IdMarker for SessionMarker {}
impl IdMarker for StreamMarker {}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Id<T: IdMarker> {
value: Uuid,
_marker: PhantomData<T>,
}
impl<T: IdMarker> Id<T> {
#[must_use]
pub fn new() -> Self {
Self {
value: Uuid::new_v4(),
_marker: PhantomData,
}
}
#[must_use]
pub fn from_uuid(uuid: Uuid) -> Self {
Self {
value: uuid,
_marker: PhantomData,
}
}
pub fn from_string(s: &str) -> Result<Self, uuid::Error> {
Uuid::parse_str(s).map(Self::from_uuid)
}
#[must_use]
pub fn as_uuid(&self) -> Uuid {
self.value
}
#[must_use]
pub fn as_str(&self) -> String {
self.value.to_string()
}
}
impl<T: IdMarker> Default for Id<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: IdMarker> fmt::Debug for Id<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple(std::any::type_name::<Self>())
.field(&self.value)
.finish()
}
}
impl<T: IdMarker> fmt::Display for Id<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl<T: IdMarker> From<Uuid> for Id<T> {
fn from(uuid: Uuid) -> Self {
Self::from_uuid(uuid)
}
}
impl<T: IdMarker> From<Id<T>> for Uuid {
fn from(id: Id<T>) -> Self {
id.value
}
}
pub type SessionId = Id<SessionMarker>;
pub type StreamId = Id<StreamMarker>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_id_creation() {
let id1 = SessionId::new();
let id2 = SessionId::new();
assert_ne!(id1, id2);
assert_eq!(id1.as_uuid().get_version_num(), 4);
}
#[test]
fn test_id_from_string() {
let uuid_str = "550e8400-e29b-41d4-a716-446655440000";
let id = SessionId::from_string(uuid_str).unwrap();
assert_eq!(id.as_str(), uuid_str);
}
#[test]
fn test_id_from_invalid_string() {
let result = SessionId::from_string("invalid-uuid");
assert!(result.is_err());
}
#[test]
fn test_different_id_types_are_distinct() {
let session_uuid = Uuid::new_v4();
let session_id = SessionId::from_uuid(session_uuid);
let stream_id = StreamId::from_uuid(session_uuid);
assert_eq!(session_id.as_uuid(), stream_id.as_uuid());
}
#[test]
fn test_id_default() {
let id = SessionId::default();
assert_eq!(id.as_uuid().get_version_num(), 4);
}
#[test]
fn test_id_debug_display() {
let id = SessionId::new();
let debug_str = format!("{:?}", id);
assert!(debug_str.contains("Id<"));
let display_str = format!("{}", id);
assert!(Uuid::parse_str(&display_str).is_ok());
}
#[test]
fn test_id_from_uuid_conversion() {
let uuid = Uuid::new_v4();
let id: SessionId = uuid.into();
let back: Uuid = id.into();
assert_eq!(uuid, back);
}
#[test]
fn test_stream_id() {
let id1 = StreamId::new();
let id2 = StreamId::new();
assert_ne!(id1, id2);
}
}