use crate::{MAX_PO, ProximityOrder};
use derive_more::{Display, Into};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Display, Into)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
#[display("bin={_0}")]
#[into(u8, usize)]
pub struct Bin(u8);
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum BinError {
#[error("bin index {raw} exceeds MAX_PO ({max})")]
OutOfRange {
raw: u8,
max: u8,
},
}
impl Bin {
pub const ZERO: Self = Self(0);
pub const MAX: Self = Self(MAX_PO);
pub const COUNT: usize = MAX_PO as usize + 1;
#[inline]
pub(crate) const fn new_unchecked(raw: u8) -> Self {
debug_assert!(raw <= MAX_PO);
Self(raw)
}
#[inline]
pub const fn new(raw: u8) -> Result<Self, BinError> {
if raw <= MAX_PO {
Ok(Self(raw))
} else {
Err(BinError::OutOfRange { raw, max: MAX_PO })
}
}
#[inline]
pub const fn get(self) -> u8 {
self.0
}
#[inline]
pub const fn as_index(self) -> usize {
self.0 as usize
}
}
impl TryFrom<u8> for Bin {
type Error = BinError;
fn try_from(raw: u8) -> Result<Self, Self::Error> {
Self::new(raw)
}
}
impl From<ProximityOrder> for Bin {
fn from(po: ProximityOrder) -> Self {
Self(po.get())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn min_max_count() {
assert_eq!(Bin::ZERO.get(), 0);
assert_eq!(Bin::MAX.get(), MAX_PO);
assert_eq!(Bin::COUNT, MAX_PO as usize + 1);
}
#[test]
fn new_in_range_ok() {
assert!(Bin::new(0).is_ok());
assert!(Bin::new(MAX_PO).is_ok());
}
#[test]
fn new_out_of_range_errs() {
let err = Bin::new(MAX_PO + 1).unwrap_err();
assert!(matches!(err, BinError::OutOfRange { raw: 32, max: 31 }));
}
#[test]
fn from_proximity_order() {
let po = ProximityOrder::new(7).unwrap();
let bin = Bin::from(po);
assert_eq!(bin.get(), 7);
}
#[test]
fn display_shows_bin() {
assert_eq!(format!("{}", Bin::new(3).unwrap()), "bin=3");
}
}