1use crate::misc::serde_ext::{self, bytes_wrapper};
4
5#[allow( clippy::derived_hash_with_manual_eq,
9 reason = "the manual PartialEq implementation agrees with the default one, but is constant time"
10)]
11#[derive(Clone, Copy, Debug, Eq, Hash, serde::Serialize, serde::Deserialize)]
12#[serde(transparent)]
13pub struct Id {
14 inner: bytes_wrapper::B64UU<serde_ext::ByteArray<32>>,
15}
16
17impl Id {
18 pub fn random() -> Self {
20 crate::misc::crypto::random_32_bytes().into()
21 }
22
23 pub fn as_slice(&self) -> &[u8] {
25 self.inner.as_slice()
26 }
27}
28
29impl From<[u8; 32]> for Id {
30 fn from(bytes: [u8; 32]) -> Self {
31 Id {
32 inner: serde_ext::ByteArray::<32>::from(bytes).into(),
33 }
34 }
35}
36
37impl core::str::FromStr for Id {
38 type Err = <bytes_wrapper::B64UU<[u8; 32]> as core::str::FromStr>::Err;
39
40 fn from_str(s: &str) -> Result<Self, Self::Err> {
41 Ok(Id { inner: s.parse()? })
42 }
43}
44
45impl std::fmt::Display for Id {
46 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
47 write!(f, "{}", self.inner)
48 }
49}
50
51impl PartialEq for Id {
53 fn eq(&self, other: &Id) -> bool {
54 subtle::ConstantTimeEq::ct_eq(self.inner.as_slice(), other.inner.as_slice()).into()
55 }
56}
57
58impl PartialOrd for Id {
59 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
60 Some(self.cmp(other))
61 }
62}
63
64impl Ord for Id {
65 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
66 self.inner.as_slice().cmp(other.inner.as_slice())
67 }
68}
69
70impl crate::common::secret::DigestibleSecret for Id {
71 fn as_bytes(&self) -> &[u8] {
72 self.as_slice()
73 }
74}