use {
super::*,
std::{
cmp::Ordering,
fmt,
hash::{Hash, Hasher},
marker::PhantomData,
str::FromStr,
},
uuid::Uuid,
};
pub struct Id<O: Identifiable> {
uuid: Uuid,
phantom: PhantomData<O>,
}
impl<O: Identifiable> PartialEq for Id<O> {
fn eq(&self, other: &Self) -> bool {
self.uuid == other.uuid
}
}
impl<O: Identifiable> Eq for Id<O> {}
impl<O: Identifiable> fmt::Display for Id<O> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}_{}", self.class().prefix(), &self.uuid.hyphenated())
}
}
impl<O: Identifiable> fmt::Debug for Id<O> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Id")
.field("uuid", &self.uuid)
.field("class", &self.class().prefix())
.finish()
}
}
impl<O: Identifiable> Clone for Id<O> {
fn clone(&self) -> Self {
*self
}
}
impl<O: Identifiable> Copy for Id<O> {}
impl<O: Identifiable> PartialOrd for Id<O> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<O: Identifiable> Ord for Id<O> {
fn cmp(&self, other: &Self) -> Ordering {
self.uuid.cmp(&other.uuid)
}
}
impl<O: Identifiable> Id<O> {
pub fn class(&self) -> IdClass {
<O as Identifiable>::class()
}
pub fn uuid(&self) -> Uuid {
self.uuid
}
pub fn db_id(&self) -> String {
self.uuid.hyphenated().to_string()
}
pub fn public_id(&self) -> String {
self.to_string()
}
pub fn from_public_id(public_id: &str) -> Result<Self, IdError> {
let db_id = <O as Identifiable>::class().strip_prefix(public_id)?;
let uuid = Uuid::try_parse(db_id).map_err(|_| IdError::InvalidFormat)?;
Ok(Self::unchecked(uuid))
}
pub fn from_db_id(db_id: &str) -> Result<Self, IdError> {
let uuid = Uuid::try_parse(db_id).map_err(|_| IdError::InvalidFormat)?;
Ok(Self::unchecked(uuid))
}
pub(crate) fn unchecked(uuid: Uuid) -> Self {
Self {
uuid,
phantom: PhantomData,
}
}
pub fn random_v4() -> Self {
Self::unchecked(Uuid::new_v4())
}
}
impl<O: Identifiable> FromStr for Id<O> {
type Err = IdError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::from_public_id(s)
}
}
impl<O: Identifiable> Hash for Id<O> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.uuid().hash(state);
}
}
#[test]
fn id_sorting() {
#[derive(Debug, Identifiable)]
#[kind(class = "Ex")]
pub struct E {}
let mut ids = Vec::<Id<E>>::new();
for _ in 0..10 {
ids.push(Id::random_v4());
}
let mut ids_by_string = ids.clone();
ids.sort();
ids_by_string.sort_by_key(|id| id.to_string());
for (a, b) in ids.iter().zip(ids_by_string.iter()) {
assert_eq!(a, b);
}
let a = vec![ids[1]];
let b = vec![ids[1], ids[7]];
let c = vec![ids[3]];
let d = vec![ids[3], ids[2]];
let e = vec![ids[3], ids[2], ids[5]];
let f = vec![ids[3], ids[6]];
let g = vec![ids[3], ids[6], ids[8]];
let paths_1 = vec![
a.clone(),
b.clone(),
c.clone(),
d.clone(),
e.clone(),
f.clone(),
g.clone(),
];
let mut paths_2 = vec![d, g, c, f, b, e, a];
paths_2.sort();
for (path_1, path_2) in paths_1.iter().zip(paths_2.iter()) {
for (a, b) in path_1.iter().zip(path_2.iter()) {
assert_eq!(a, b);
}
}
}