use std::fmt::{Debug, Display, Formatter};
use bytemuck::{Pod, Zeroable};
#[cfg(feature = "serde")]
use serde_crate::{
de::{Error, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use crate::Stage;
const BELL_NAMES: &str = "1234567890ETABCDFGHJKLMNPQRSUVWYZ";
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Zeroable, Pod)]
#[repr(transparent)] pub struct Bell {
index: u8,
}
impl Bell {
pub fn from_name(c: char) -> Option<Bell> {
BELL_NAMES
.chars()
.position(|x| x == c)
.map(|v| v as u8)
.map(Bell::from_index)
}
#[inline]
pub fn from_index(index: u8) -> Bell {
assert_ne!(index, 255, "`Bell`s with index 255 can't be created.");
Bell { index }
}
pub fn from_number(number: u8) -> Option<Bell> {
number.checked_sub(1).map(Bell::from_index)
}
pub fn tenor(stage: Stage) -> Bell {
Self::from_number(stage.num_bells_u8()).unwrap()
}
pub const TREBLE: Bell = Bell { index: 0 };
pub const MAX: Bell = Bell { index: 254 };
pub fn to_char(self) -> Option<char> {
BELL_NAMES.as_bytes().get(self.index()).map(|x| *x as char)
}
#[inline]
pub fn index(self) -> usize {
self.index as usize
}
#[inline]
pub fn index_u8(self) -> u8 {
self.index
}
#[inline]
pub fn number(self) -> u8 {
self.index + 1 }
pub fn name(self) -> String {
match self.to_char() {
None => format!("<{}>", self.number()),
Some(c) => c.to_string(),
}
}
}
impl Debug for Bell {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Bell({})", self)
}
}
impl Display for Bell {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name())
}
}
impl Bell {
#[track_caller]
fn from_i16_idx(idx: i16) -> Self {
debug_assert!(
idx >= u8::MIN as i16,
"Integer underflow while adding to `Bell`"
);
debug_assert!(
idx <= u8::MAX as i16,
"Integer overflow while adding to `Bell`"
);
Self { index: idx as u8 }
}
}
impl std::ops::Add<i16> for Bell {
type Output = Bell;
#[track_caller]
fn add(self, rhs: i16) -> Self::Output {
Self::from_i16_idx(self.index as i16 + rhs)
}
}
impl std::ops::AddAssign<i16> for Bell {
#[track_caller]
fn add_assign(&mut self, rhs: i16) {
*self = *self + rhs;
}
}
impl std::ops::Sub<i16> for Bell {
type Output = Bell;
#[track_caller]
fn sub(self, rhs: i16) -> Self::Output {
Self::from_i16_idx(self.index as i16 - rhs)
}
}
impl std::ops::SubAssign<i16> for Bell {
#[track_caller]
fn sub_assign(&mut self, rhs: i16) {
*self = *self - rhs;
}
}
#[cfg(feature = "serde")]
struct BellVisitor;
#[cfg(feature = "serde")]
impl<'de> Visitor<'de> for BellVisitor {
type Value = Bell;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a non-negative integer, or a bell name")
}
fn visit_u8<E>(self, val: u8) -> Result<Self::Value, E>
where
E: Error,
{
if val > 0 {
Ok(Bell::from_number(val).unwrap())
} else {
Err(E::custom(format!("invalid Bell number: {}", val)))
}
}
fn visit_char<E>(self, v: char) -> Result<Self::Value, E>
where
E: Error,
{
Bell::from_name(v).ok_or_else(|| E::custom(format!("'{}' is not a bell name", v)))
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: Error,
{
if v.len() != 1 {
return Err(E::custom(format!("'{}' is not a bell name", v)));
}
v.chars()
.next()
.and_then(Bell::from_name)
.ok_or_else(|| E::custom(format!("'{}' is not a bell name", v)))
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Bell {
fn deserialize<D>(deserializer: D) -> Result<Bell, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_u64(BellVisitor)
}
}
#[cfg(feature = "serde")]
impl Serialize for Bell {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_u64(self.index as u64)
}
}
#[cfg(test)]
mod tests {
use super::Bell;
#[test]
#[should_panic]
fn from_index_panic() {
Bell::from_index(255);
}
}