use std::{fmt::Display, str::FromStr}; use rand::{distributions::Standard, prelude::Distribution, Rng}; use crate::{Error, Id, SingleId}; #[derive(Debug, Clone, PartialEq)] pub struct DoubleId{ inner: (SingleId, SingleId) } impl DoubleId { #[cfg(test)] pub fn validate(&self) -> bool { self.inner.0.validate() && self.inner.1.validate() && (u32::from(self.clone()) < Self::SIZE) } } impl Id for DoubleId{ type SizeType = u32; const SIZE: Self::SizeType = (SingleId::SIZE as u32).pow(2); /// ``` /// use caretta_id::{Id, DoubleId}; /// use std::str::FromStr; /// /// assert_eq!(DoubleId::NIL, DoubleId::from_str("000-000").unwrap()); /// assert_eq!(DoubleId::NIL, DoubleId::try_from(0).unwrap()); /// ``` const NIL: Self = Self{ inner: (SingleId::NIL, SingleId::NIL) }; /// ``` /// use caretta_id::{Id, DoubleId}; /// use std::str::FromStr; /// /// assert_eq!(DoubleId::MAX, DoubleId::from_str("zzz-zzz").unwrap()); /// assert_eq!(DoubleId::MAX, DoubleId::try_from(1291467968).unwrap()); /// ``` const MAX: Self = Self{ inner: (SingleId::MAX, SingleId::MAX) }; } impl Display for DoubleId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}-{}", self.inner.0, self.inner.1) } } impl FromStr for DoubleId { type Err = Error; fn from_str(s: &str) -> Result { Ok(Self { inner : match s.len() { 7 => { match s.chars().nth(3).unwrap() { '-' => { Ok((SingleId::from_str(&s[0..3])?, SingleId::from_str(&s[4..7])?)) }, x => { Err(Error::InvalidDelimiter(x)) } } } 6 => { Ok((SingleId::from_str(&s[0..3])?,SingleId::from_str(&s[3..6])?)) } _ => Err(Error::InvalidLength(s.to_string())) }? }) } } impl Distribution for Standard { fn sample(&self, rng: &mut R) -> DoubleId { DoubleId { inner: (rng.r#gen(), rng.r#gen()) } } } impl TryFrom for DoubleId { type Error = Error; fn try_from(value: u32) -> Result { if value < Self::SIZE { Ok(Self{ inner: ( SingleId::try_from(u16::try_from(value/(SingleId::SIZE as u32)).unwrap())?, SingleId::try_from(u16::try_from(value % (SingleId::SIZE as u32)).unwrap())? )}) } else { Err(Error::OutsideOfRange(value as u64)) } } } impl From for u32 { fn from(value: DoubleId) -> Self { u32::from(u16::from(value.inner.0)) * u32::from(SingleId::SIZE) + u32::from(u16::from(value.inner.1)) } } #[cfg(test)] mod tests { use super::*; fn assert_random(rand: &mut R) where R: Rng { let chunk: SingleId = rand.r#gen(); let s = chunk.to_string(); assert_eq!(chunk,SingleId::from_str(&s).unwrap()) } #[test] fn random_x10() { let mut rng = rand::thread_rng(); for _ in 0..10 { assert_random(&mut rng); } } }