tycho_network/overlay/
overlay_id.rs

1use std::borrow::Borrow;
2use std::str::FromStr;
3
4use rand::Rng;
5use tl_proto::{TlRead, TlWrite};
6
7#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, TlRead, TlWrite)]
8#[repr(transparent)]
9pub struct OverlayId(pub [u8; 32]);
10
11impl OverlayId {
12    pub const fn wrap(bytes: &[u8; 32]) -> &Self {
13        // SAFETY: `[u8; 32]` has the same layout as `OverlayId`.
14        unsafe { &*(bytes as *const [u8; 32]).cast::<Self>() }
15    }
16
17    #[inline]
18    pub fn as_bytes(&self) -> &[u8; 32] {
19        &self.0
20    }
21
22    #[inline]
23    pub fn to_bytes(self) -> [u8; 32] {
24        self.0
25    }
26}
27
28impl Borrow<[u8; 32]> for OverlayId {
29    #[inline]
30    fn borrow(&self) -> &[u8; 32] {
31        &self.0
32    }
33}
34
35impl<'a> TlRead<'a> for &'a OverlayId {
36    type Repr = tl_proto::Boxed;
37
38    #[inline]
39    fn read_from(packet: &mut &'a [u8]) -> tl_proto::TlResult<Self> {
40        <_>::read_from(packet).map(OverlayId::wrap)
41    }
42}
43
44impl rand::distr::Distribution<OverlayId> for rand::distr::StandardUniform {
45    #[inline]
46    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> OverlayId {
47        OverlayId(rand::distr::StandardUniform.sample(rng))
48    }
49}
50
51impl std::fmt::Display for OverlayId {
52    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53        let len = f.precision().unwrap_or(32);
54        for byte in self.0.iter().take(len) {
55            write!(f, "{byte:02x}")?;
56        }
57        Ok(())
58    }
59}
60
61impl std::fmt::Debug for OverlayId {
62    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63        write!(f, "OverlayId({self})")
64    }
65}
66
67impl FromStr for OverlayId {
68    type Err = hex::FromHexError;
69
70    fn from_str(s: &str) -> Result<Self, Self::Err> {
71        let mut overlay_id = OverlayId([0; 32]);
72        hex::decode_to_slice(s, &mut overlay_id.0).map(|_| overlay_id)
73    }
74}
75
76impl serde::Serialize for OverlayId {
77    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
78    where
79        S: serde::Serializer,
80    {
81        if serializer.is_human_readable() {
82            serializer.collect_str(self)
83        } else {
84            self.0.serialize(serializer)
85        }
86    }
87}
88
89impl<'de> serde::Deserialize<'de> for OverlayId {
90    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
91    where
92        D: serde::Deserializer<'de>,
93    {
94        if deserializer.is_human_readable() {
95            deserializer.deserialize_str(tycho_util::serde_helpers::StrVisitor::new())
96        } else {
97            <[u8; 32]>::deserialize(deserializer).map(Self)
98        }
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn serde() {
108        const SOME_ID: &str = "5d09fe251943525a30f471791d5b4fea1298613f52ad2ad6d985fed05eb00533";
109
110        let from_json: OverlayId = serde_json::from_str(&format!("\"{SOME_ID}\"")).unwrap();
111        let from_str = OverlayId::from_str(SOME_ID).unwrap();
112        assert_eq!(from_json, from_str);
113
114        let to_json = serde_json::to_string(&from_json).unwrap();
115        let from_json: OverlayId = serde_json::from_str(&to_json).unwrap();
116        assert_eq!(from_json, from_str);
117    }
118}