pub trait Accidental
where
Self: 'static + AsRef<str> + Send + Sync + core::fmt::Debug + core::fmt::Display,
{
private! {}
fn new() -> Self
where
Self: Sized;
#[allow(clippy::should_implement_trait)]
fn from_str(s: &str) -> Result<Self, crate::error::Error>
where
Self: Sized + core::str::FromStr<Err = crate::error::Error>,
{
s.parse::<Self>()
}
fn name(&self) -> &str;
fn symbol(&self) -> char;
}
macro_rules! accidental {
(@impl $(#[$meta:meta])* $vis:vis $type:ident $name:ident = $sym:literal $(;)?) => {
unit_type! {
$(#[$meta])*
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
$vis $type $name
}
impl $name {
pub const NAME: &'static str = stringify!($name);
pub fn of<T>() -> bool
where
T: 'static,
{
::core::any::TypeId::of::<T>() == ::core::any::TypeId::of::<Self>()
}
pub const fn symbol(&self) -> char {
$sym
}
pub fn name(&self) -> &str {
stringify!($name)
}
}
impl $crate::pitch::Accidental for $name {
seal! {}
fn new() -> Self {
Self
}
fn name(&self) -> &str {
self.name()
}
fn symbol(&self) -> char {
self.symbol()
}
}
impl AsRef<str> for $name {
fn as_ref(&self) -> &str {
stringify!($name)
}
}
impl ::core::fmt::Debug for $name {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
write!(f, "{}", self.symbol())
}
}
impl ::core::fmt::Display for $name {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
write!(f, "{}", self.symbol())
}
}
};
($($vis:vis $type:ident $name:ident $(= $sym:literal)?);* $(;)?) => {
$(accidental! { @impl $vis $type $name $(= $sym)? })*
};
}
accidental! {
pub struct Flat = '♭';
pub struct Sharp = '♯';
}
#[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(rename_all = "snake_case")
)]
#[repr(transparent)]
pub struct Natural;
impl Natural {
pub const fn new() -> Self {
Self
}
pub fn of<T>() -> bool
where
T: 'static,
{
::core::any::TypeId::of::<T>() == ::core::any::TypeId::of::<Self>()
}
pub const fn name(&self) -> &str {
"Natural"
}
pub const fn symbol(&self) -> char {
'♮'
}
}
impl Accidental for Natural {
seal! {}
fn new() -> Self {
Self::new()
}
fn name(&self) -> &str {
self.name()
}
fn symbol(&self) -> char {
self.symbol()
}
}
impl AsRef<str> for Natural {
fn as_ref(&self) -> &str {
"Natural"
}
}
impl AsRef<char> for Natural {
fn as_ref(&self) -> &char {
&'♮'
}
}
impl core::borrow::Borrow<str> for Natural {
fn borrow(&self) -> &str {
self.as_ref()
}
}
impl core::borrow::Borrow<char> for Natural {
fn borrow(&self) -> &char {
self.as_ref()
}
}
impl core::ops::Deref for Natural {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl core::str::FromStr for Natural {
type Err = crate::error::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() || s.to_lowercase() == "natural" || s == "♮" {
Ok(Natural)
} else {
Err(anyhow::anyhow!("Unable to parse a natural note : {}", s).into())
}
}
}
impl core::fmt::Debug for Natural {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(self.name())
}
}
impl core::fmt::Display for Natural {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.symbol())
}
}
#[cfg(feature = "alloc")]
impl ::core::str::FromStr for Flat {
type Err = crate::error::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.to_lowercase() == "flat" || s == "♭" || s == "b" {
Ok(Self)
} else {
Err(anyhow::anyhow!("Invalid accidental string: {}", s).into())
}
}
}
#[cfg(feature = "alloc")]
impl ::core::str::FromStr for Sharp {
type Err = crate::error::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.to_lowercase() == "sharp" || s == "♯" || s == "#" {
Ok(Self)
} else {
Err(anyhow::anyhow!("Invalid accidental string: {}", s).into())
}
}
}