Skip to main content

nectar_primitives/
proximity_order.rs

1//! Typed Kademlia proximity order (PO) in the range `0..=MAX_PO`.
2//!
3//! See [`MAX_PO`] for the standard cap (31) and
4//! [`address`](crate::address) for the derivation from two [`SwarmAddress`es](crate::SwarmAddress).
5
6use crate::MAX_PO;
7use derive_more::{Display, Into};
8
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12/// Typed proximity order, `0..=MAX_PO` (= 0..=31).
13///
14/// Distinguished from [`Bin`](crate::Bin) at the type level even though they
15/// share a representation. PO is the metric, Bin is the routing-table slot.
16#[repr(transparent)]
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Display, Into)]
18#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
19#[cfg_attr(feature = "serde", serde(transparent))]
20#[display("po={_0}")]
21#[into(u8, usize)]
22pub struct ProximityOrder(u8);
23
24/// Errors from constructing a [`ProximityOrder`].
25#[non_exhaustive]
26#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
27pub enum ProximityOrderError {
28    /// Value exceeded [`MAX_PO`].
29    #[error("proximity order {raw} exceeds MAX_PO ({max})")]
30    OutOfRange {
31        /// The rejected value.
32        raw: u8,
33        /// The maximum permitted value ([`MAX_PO`]).
34        max: u8,
35    },
36}
37
38impl ProximityOrder {
39    /// The smallest proximity order.
40    pub const MIN: Self = Self(0);
41
42    /// The largest proximity order ([`MAX_PO`] = 31).
43    pub const MAX: Self = Self(MAX_PO);
44
45    /// Construct without bounds checking. Caller must ensure `raw <= MAX_PO`.
46    ///
47    /// Used by internal code that already validated the range (e.g.
48    /// `SwarmAddress::proximity_order`, which can never exceed `MAX_PO`).
49    #[inline]
50    pub(crate) const fn new_unchecked(raw: u8) -> Self {
51        debug_assert!(raw <= MAX_PO);
52        Self(raw)
53    }
54
55    /// Construct from a raw byte, validating the range.
56    #[inline]
57    pub const fn new(raw: u8) -> Result<Self, ProximityOrderError> {
58        if raw <= MAX_PO {
59            Ok(Self(raw))
60        } else {
61            Err(ProximityOrderError::OutOfRange { raw, max: MAX_PO })
62        }
63    }
64
65    /// Underlying byte value.
66    #[inline]
67    pub const fn get(self) -> u8 {
68        self.0
69    }
70}
71
72impl TryFrom<u8> for ProximityOrder {
73    type Error = ProximityOrderError;
74
75    fn try_from(raw: u8) -> Result<Self, Self::Error> {
76        Self::new(raw)
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    #[test]
85    fn min_max_consts() {
86        assert_eq!(ProximityOrder::MIN.get(), 0);
87        assert_eq!(ProximityOrder::MAX.get(), MAX_PO);
88    }
89
90    #[test]
91    fn new_in_range_ok() {
92        assert!(ProximityOrder::new(0).is_ok());
93        assert!(ProximityOrder::new(MAX_PO).is_ok());
94    }
95
96    #[test]
97    fn new_out_of_range_errs() {
98        let err = ProximityOrder::new(MAX_PO + 1).unwrap_err();
99        assert!(matches!(
100            err,
101            ProximityOrderError::OutOfRange { raw: 32, max: 31 }
102        ));
103    }
104
105    #[test]
106    fn display_shows_po() {
107        assert_eq!(format!("{}", ProximityOrder::new(7).unwrap()), "po=7");
108    }
109
110    #[test]
111    fn try_from_byte() {
112        let po: ProximityOrder = 5u8.try_into().unwrap();
113        assert_eq!(po.get(), 5);
114    }
115}