caretta-sync/id/src/double.rs

129 lines
3.4 KiB
Rust
Raw Normal View History

2025-09-13 08:39:06 +09:00
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<Self, Self::Err> {
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<DoubleId> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> DoubleId {
DoubleId {
inner: (rng.r#gen(), rng.r#gen())
}
}
}
impl TryFrom<u32> for DoubleId {
type Error = Error;
fn try_from(value: u32) -> Result<Self, Self::Error> {
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<DoubleId> 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<R>(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);
}
}
}