use std::fmt::Display;
use thiserror::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Handle(u16);
#[derive(Debug, Error)]
pub enum HandleError {
#[error("{0:#X} is reserved")]
Reserved(u16),
#[error("value too large to be a valid track handle")]
TooLarge,
}
impl TryFrom<u16> for Handle {
type Error = HandleError;
fn try_from(value: u16) -> Result<Self, Self::Error> {
if value == 0 {
Err(HandleError::Reserved(value))?
}
Ok(Self(value))
}
}
impl TryFrom<u32> for Handle {
type Error = HandleError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
let value: u16 = value.try_into().map_err(|_| HandleError::TooLarge)?;
value.try_into()
}
}
impl From<Handle> for u16 {
fn from(handle: Handle) -> Self {
handle.0
}
}
impl From<Handle> for u32 {
fn from(handle: Handle) -> Self {
handle.0 as u32
}
}
impl Display for Handle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{0:#X}", self.0)
}
}
#[derive(Debug, Default)]
pub struct HandleAllocator {
value: u16,
}
impl HandleAllocator {
pub fn get(&mut self) -> Option<Handle> {
self.value = self.value.checked_add(1)?;
Handle(self.value).into()
}
}
#[cfg(test)]
impl fake::Dummy<fake::Faker> for Handle {
fn dummy_with_rng<R: rand::Rng + ?Sized>(_: &fake::Faker, rng: &mut R) -> Self {
Self::try_from(rng.random_range(1..u16::MAX)).unwrap()
}
}