use std::{
any::TypeId,
fmt,
hash::{Hash, Hasher},
};
use rustc_hash::FxHasher;
use crate::puppet::Puppet;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Id(u64);
impl Id {
#[must_use]
pub fn new<T>() -> Self
where
T: 'static,
{
TypeId::of::<T>().into()
}
}
impl From<TypeId> for Id {
fn from(type_id: TypeId) -> Self {
let mut hasher = FxHasher::default();
type_id.hash(&mut hasher);
Self(hasher.finish())
}
}
impl fmt::Display for Id {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::hash::Hash for Id {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
state.write_u64(self.0);
}
}
#[derive(Clone, Copy, Eq)]
pub struct Pid {
pub(crate) id: Id,
pub(crate) name_fn: fn() -> String,
}
impl PartialEq for Pid {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl PartialOrd for Pid {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Pid {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.id.cmp(&other.id)
}
}
impl Pid {
#[must_use]
pub fn new<P>() -> Self
where
P: Puppet,
{
let id = Id::new::<P>();
Self {
id,
name_fn: Self::_name::<P>,
}
}
#[must_use]
pub fn as_id(&self) -> Id {
self.id
}
fn _name<T>() -> String
where
T: 'static,
{
std::any::type_name::<T>().to_owned()
}
#[must_use]
pub fn name(&self) -> String {
(self.name_fn)()
}
}
impl fmt::Display for Pid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
impl fmt::Debug for Pid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Id")
.field("id", &self.id)
.field("name", &self.name())
.finish_non_exhaustive()
}
}
impl std::hash::Hash for Pid {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
state.write_u64(self.id.0);
}
}
impl From<Pid> for String {
fn from(value: Pid) -> Self {
value.name()
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use super::*;
#[derive(Clone, Default)]
struct FirstPuppet;
impl Puppet for FirstPuppet {
type Supervision = OneToOne;
}
#[derive(Clone, Default)]
struct SecondPuppet;
impl Puppet for SecondPuppet {
type Supervision = OneToOne;
}
#[test]
fn test_id_new() {
let id1 = Id::new::<i32>();
let id2 = Id::new::<String>();
assert_ne!(id1, id2);
let id3 = Id::new::<i32>();
assert_eq!(id1, id3);
}
#[test]
fn test_pid_name() {
let pid = Pid::new::<FirstPuppet>();
assert!(pid.name().ends_with("FirstPuppet"));
}
#[test]
fn test_pid_eq() {
let pid1 = Pid::new::<FirstPuppet>();
let pid2 = Pid::new::<FirstPuppet>();
assert_eq!(pid1, pid2);
let pid3 = Pid::new::<SecondPuppet>();
assert_ne!(pid1, pid3);
}
#[test]
fn test_pid_partial_ord() {
let pid1 = Pid::new::<FirstPuppet>();
let pid2 = Pid::new::<FirstPuppet>();
assert_eq!(pid1.partial_cmp(&pid2), Some(std::cmp::Ordering::Equal));
let pid3 = Pid::new::<SecondPuppet>();
assert_eq!(
pid1.partial_cmp(&pid3),
pid1.as_id().partial_cmp(&pid3.as_id())
);
}
#[test]
fn test_pid_hash() {
let pid1 = Pid::new::<FirstPuppet>();
let pid2 = Pid::new::<FirstPuppet>();
let mut hasher1 = std::collections::hash_map::DefaultHasher::new();
let mut hasher2 = std::collections::hash_map::DefaultHasher::new();
pid1.hash(&mut hasher1);
pid2.hash(&mut hasher2);
assert_eq!(hasher1.finish(), hasher2.finish());
let pid3 = Pid::new::<SecondPuppet>();
let mut hasher3 = std::collections::hash_map::DefaultHasher::new();
pid3.hash(&mut hasher3);
assert_ne!(hasher1.finish(), hasher3.finish());
}
#[test]
fn test_pid_from_string() {
let pid = Pid::new::<FirstPuppet>();
let pid_string: String = pid.into();
assert!(pid_string.ends_with("FirstPuppet"));
}
}