1use crate::{MAX_PO, ProximityOrder};
9use derive_more::{Display, Into};
10
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14#[repr(transparent)]
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Display, Into)]
17#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
18#[cfg_attr(feature = "serde", serde(transparent))]
19#[display("bin={_0}")]
20#[into(u8, usize)]
21pub struct Bin(u8);
22
23#[non_exhaustive]
25#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
26pub enum BinError {
27 #[error("bin index {raw} exceeds MAX_PO ({max})")]
29 OutOfRange {
30 raw: u8,
32 max: u8,
34 },
35}
36
37impl Bin {
38 pub const ZERO: Self = Self(0);
40
41 pub const MAX: Self = Self(MAX_PO);
43
44 pub const COUNT: usize = MAX_PO as usize + 1;
46
47 #[inline]
49 pub(crate) const fn new_unchecked(raw: u8) -> Self {
50 debug_assert!(raw <= MAX_PO);
51 Self(raw)
52 }
53
54 #[inline]
56 pub const fn new(raw: u8) -> Result<Self, BinError> {
57 if raw <= MAX_PO {
58 Ok(Self(raw))
59 } else {
60 Err(BinError::OutOfRange { raw, max: MAX_PO })
61 }
62 }
63
64 #[inline]
66 pub const fn get(self) -> u8 {
67 self.0
68 }
69
70 #[inline]
72 pub const fn as_index(self) -> usize {
73 self.0 as usize
74 }
75}
76
77impl TryFrom<u8> for Bin {
78 type Error = BinError;
79
80 fn try_from(raw: u8) -> Result<Self, Self::Error> {
81 Self::new(raw)
82 }
83}
84
85impl From<ProximityOrder> for Bin {
86 fn from(po: ProximityOrder) -> Self {
89 Self(po.get())
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn min_max_count() {
100 assert_eq!(Bin::ZERO.get(), 0);
101 assert_eq!(Bin::MAX.get(), MAX_PO);
102 assert_eq!(Bin::COUNT, MAX_PO as usize + 1);
103 }
104
105 #[test]
106 fn new_in_range_ok() {
107 assert!(Bin::new(0).is_ok());
108 assert!(Bin::new(MAX_PO).is_ok());
109 }
110
111 #[test]
112 fn new_out_of_range_errs() {
113 let err = Bin::new(MAX_PO + 1).unwrap_err();
114 assert!(matches!(err, BinError::OutOfRange { raw: 32, max: 31 }));
115 }
116
117 #[test]
118 fn from_proximity_order() {
119 let po = ProximityOrder::new(7).unwrap();
120 let bin = Bin::from(po);
121 assert_eq!(bin.get(), 7);
122 }
123
124 #[test]
125 fn display_shows_bin() {
126 assert_eq!(format!("{}", Bin::new(3).unwrap()), "bin=3");
127 }
128}