| 
									
										
										
										
											2025-09-13 08:39:06 +09:00
										 |  |  | use std::{fmt::Display, str::FromStr};
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | use rand::{distributions::Standard, prelude::Distribution, Rng};
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-13 12:29:59 +09:00
										 |  |  | use crate::{utils::is_delimiter, Error, Id, SingleId};
 | 
					
						
							| 
									
										
										
										
											2025-09-13 08:39:06 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | #[derive(Debug, Clone, PartialEq)]
 | 
					
						
							|  |  |  | pub struct DoubleId{
 | 
					
						
							|  |  |  |     inner: (SingleId, SingleId)
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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) 
 | 
					
						
							|  |  |  |     };
 | 
					
						
							| 
									
										
										
										
											2025-09-13 12:29:59 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     #[cfg(test)]
 | 
					
						
							|  |  |  |     fn is_valid(&self) -> bool {
 | 
					
						
							| 
									
										
										
										
											2025-09-15 17:05:30 +09:00
										 |  |  |         self.inner.0.is_valid() && self.inner.1.is_valid() && (u32::from(self) < Self::SIZE)
 | 
					
						
							| 
									
										
										
										
											2025-09-13 12:29:59 +09:00
										 |  |  |     }
 | 
					
						
							| 
									
										
										
										
											2025-09-13 08:39:06 +09:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 => {
 | 
					
						
							| 
									
										
										
										
											2025-09-13 12:29:59 +09:00
										 |  |  |                     let delimiter = s[3..4].chars().next().unwrap();
 | 
					
						
							|  |  |  |                     if is_delimiter(delimiter) {
 | 
					
						
							|  |  |  |                         Ok((SingleId::from_str(&s[0..3])?,SingleId::from_str(&s[4..7])?))
 | 
					
						
							|  |  |  |                     } else {
 | 
					
						
							|  |  |  |                         Err(Error::InvalidDelimiter{
 | 
					
						
							|  |  |  |                             found: vec![delimiter],
 | 
					
						
							|  |  |  |                             raw: s.to_string()
 | 
					
						
							|  |  |  |                         })
 | 
					
						
							| 
									
										
										
										
											2025-09-13 08:39:06 +09:00
										 |  |  |                     }
 | 
					
						
							|  |  |  |                     
 | 
					
						
							|  |  |  |                 }
 | 
					
						
							|  |  |  |                 6 => {
 | 
					
						
							|  |  |  |                     Ok((SingleId::from_str(&s[0..3])?,SingleId::from_str(&s[3..6])?))
 | 
					
						
							|  |  |  |                 }
 | 
					
						
							| 
									
										
										
										
											2025-09-13 12:29:59 +09:00
										 |  |  |                 x => Err(Error::InvalidLength{
 | 
					
						
							|  |  |  |                     expected: (6, 7),
 | 
					
						
							|  |  |  |                     found: x,
 | 
					
						
							|  |  |  |                     raw: s.to_string()
 | 
					
						
							|  |  |  |                 })
 | 
					
						
							| 
									
										
										
										
											2025-09-13 08:39:06 +09:00
										 |  |  |             }?
 | 
					
						
							|  |  |  |         })
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 {
 | 
					
						
							| 
									
										
										
										
											2025-09-13 12:29:59 +09:00
										 |  |  |             Err(Error::OutsideOfRange{
 | 
					
						
							|  |  |  |                 expected: Self::SIZE as usize,
 | 
					
						
							|  |  |  |                 found: value as usize
 | 
					
						
							|  |  |  |             })
 | 
					
						
							| 
									
										
										
										
											2025-09-13 08:39:06 +09:00
										 |  |  |         }
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-15 17:05:30 +09:00
										 |  |  | 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))
 | 
					
						
							| 
									
										
										
										
											2025-09-13 08:39:06 +09:00
										 |  |  |     }
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #[cfg(test)]
 | 
					
						
							|  |  |  | mod tests {
 | 
					
						
							|  |  |  |     use super::*;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fn assert_random<R>(rand: &mut  R)
 | 
					
						
							|  |  |  |     where
 | 
					
						
							|  |  |  |         R: Rng
 | 
					
						
							|  |  |  |     {
 | 
					
						
							| 
									
										
										
										
											2025-09-13 12:29:59 +09:00
										 |  |  |         let id: DoubleId = rand.r#gen();
 | 
					
						
							|  |  |  |         assert!(id.is_valid());
 | 
					
						
							|  |  |  |         assert_eq!(id,DoubleId::from_str(&id.to_string()).unwrap());
 | 
					
						
							| 
									
										
										
										
											2025-09-15 17:05:30 +09:00
										 |  |  |         assert_eq!(id, DoubleId::try_from(u32::from(&id)).unwrap())
 | 
					
						
							| 
									
										
										
										
											2025-09-13 08:39:06 +09:00
										 |  |  |     }
 | 
					
						
							|  |  |  |     #[test]
 | 
					
						
							|  |  |  |     fn random_x10() {
 | 
					
						
							|  |  |  |         let mut rng = rand::thread_rng();
 | 
					
						
							|  |  |  |         for _ in 0..10 {
 | 
					
						
							|  |  |  |             assert_random(&mut rng);
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | }
 |