use { crate::array, arrayvec::ArrayString, rand::{RngExt, rng}, serde::{Deserialize, Deserializer, Serialize, Serializer, de}, std::{ fmt::{Debug, Display, Formatter}, num::ParseIntError, str::FromStr, }, thiserror::Error, }; #[cfg(test)] mod tests; #[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] #[repr(transparent)] pub struct Opaque { v: [u64; 3], } pub fn opaque() -> Opaque { let mut rng = rng(); Opaque { v: array::from_fn(|_| rng.random()), } } impl Opaque { pub fn to_string(self) -> ArrayString { use std::fmt::Write; let mut s = ArrayString::new(); write!(s, "{}", self).unwrap(); s } } impl Display for Opaque { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{:016x}", self.v[2])?; write!(f, "{:016x}", self.v[1])?; write!(f, "{:016x}", self.v[0])?; Ok(()) } } impl Debug for Opaque { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { Display::fmt(self, f) } } impl Serialize for Opaque { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let s = self.to_string(); serializer.serialize_str(&s) } } impl<'de> Deserialize<'de> for Opaque { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { let s = <&str>::deserialize(deserializer)?; Opaque::from_str(s).map_err(de::Error::custom) } } impl FromStr for Opaque { type Err = OpaqueError; fn from_str(s: &str) -> Result { if s.len() != OPAQUE_LEN { return Err(OpaqueError::InvalidStringLength); } if !s.is_char_boundary(OPAQUE_SEGMENT) { return Err(OpaqueError::NotAscii); } let (a, s) = s.split_at(OPAQUE_SEGMENT); if !s.is_char_boundary(OPAQUE_SEGMENT) { return Err(OpaqueError::NotAscii); } let (b, c) = s.split_at(OPAQUE_SEGMENT); let v = [ u64::from_str_radix(c, 16).map_err(OpaqueError::Parse)?, u64::from_str_radix(b, 16).map_err(OpaqueError::Parse)?, u64::from_str_radix(a, 16).map_err(OpaqueError::Parse)?, ]; Ok(Self { v }) } } pub const OPAQUE_LEN: usize = 48; const OPAQUE_SEGMENT: usize = OPAQUE_LEN / 3; #[derive(Debug, Error)] pub enum OpaqueError { #[error("The string is not exactly {OPAQUE_LEN} bytes long")] InvalidStringLength, #[error("The string is not ascii")] NotAscii, #[error("Could not parse the string as a hex number")] Parse(ParseIntError), }