rstmt_core/pitch/traits/
accidental.rs1pub trait RawAccidental:
10 'static + AsRef<str> + Send + Sync + core::fmt::Debug + core::fmt::Display
11{
12 private! {}
13
14 fn name(&self) -> &str;
15
16 fn symbol(&self) -> char;
17}
18
19pub trait Accidental: RawAccidental
20where
21 Self: Default + core::str::FromStr<Err = crate::error::Error>,
22{
23}
24macro_rules! accidental {
28 (@impl $(#[$meta:meta])* $vis:vis $type:ident $name:ident = $sym:literal $(;)?) => {
29 unit_type! {
30 $(#[$meta])*
31 #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
32 $vis $type $name
33 }
34
35 impl $name {
36 pub const NAME: &'static str = stringify!($name);
37 pub fn of<T>() -> bool
39 where
40 T: 'static,
41 {
42 ::core::any::TypeId::of::<T>() == ::core::any::TypeId::of::<Self>()
43 }
44 pub const fn symbol(&self) -> char {
46 $sym
47 }
48 pub fn name(&self) -> &str {
50 stringify!($name)
51 }
52 }
53
54 impl $crate::pitch::RawAccidental for $name {
55 seal! {}
56
57 fn name(&self) -> &str {
58 self.name()
59 }
60
61 fn symbol(&self) -> char {
62 self.symbol()
63 }
64 }
65
66 impl $crate::pitch::Accidental for $name {
67
68
69 }
70
71 impl AsRef<str> for $name {
72 fn as_ref(&self) -> &str {
73 stringify!($name)
74 }
75 }
76
77 impl ::core::fmt::Debug for $name {
78 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
79 write!(f, "{}", self.symbol())
80 }
81 }
82
83 impl ::core::fmt::Display for $name {
84 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
85 write!(f, "{}", self.symbol())
86 }
87 }
88 };
89 ($($vis:vis $type:ident $name:ident $(= $sym:literal)?);* $(;)?) => {
90 $(accidental! { @impl $vis $type $name $(= $sym)? })*
91 };
92}
93
94accidental! {
95 pub struct Flat = '♭';
96 pub struct Sharp = '♯';
97}
98
99#[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
100#[cfg_attr(
101 feature = "serde",
102 derive(serde::Deserialize, serde::Serialize),
103 serde(rename_all = "snake_case")
104)]
105#[repr(transparent)]
106pub struct Natural;
107
108impl Natural {
109 pub const fn new() -> Self {
110 Self
111 }
112 pub fn of<T>() -> bool
114 where
115 T: 'static,
116 {
117 ::core::any::TypeId::of::<T>() == ::core::any::TypeId::of::<Self>()
118 }
119
120 pub const fn name(&self) -> &str {
121 "Natural"
122 }
123
124 pub const fn symbol(&self) -> char {
125 '♮'
126 }
127}
128
129impl crate::pitch::RawAccidental for Natural {
130 seal! {}
131
132 fn name(&self) -> &str {
133 self.name()
134 }
135
136 fn symbol(&self) -> char {
137 self.symbol()
138 }
139}
140
141impl crate::pitch::Accidental for Natural {}
142
143impl AsRef<str> for Natural {
144 fn as_ref(&self) -> &str {
145 "Natural"
146 }
147}
148
149impl AsRef<char> for Natural {
150 fn as_ref(&self) -> &char {
151 &'♮'
152 }
153}
154
155impl core::borrow::Borrow<str> for Natural {
156 fn borrow(&self) -> &str {
157 self.as_ref()
158 }
159}
160
161impl core::borrow::Borrow<char> for Natural {
162 fn borrow(&self) -> &char {
163 self.as_ref()
164 }
165}
166
167impl core::ops::Deref for Natural {
168 type Target = str;
169
170 fn deref(&self) -> &Self::Target {
171 self.as_ref()
172 }
173}
174
175impl core::str::FromStr for Natural {
176 type Err = crate::error::Error;
177
178 fn from_str(s: &str) -> Result<Self, Self::Err> {
179 if s.is_empty() || s.to_lowercase() == "natural" || s == "♮" {
180 Ok(Natural)
181 } else {
182 Err(anyhow::anyhow!("Unable to parse a natural note : {}", s).into())
183 }
184 }
185}
186
187impl core::fmt::Debug for Natural {
188 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
189 f.write_str(self.name())
190 }
191}
192
193impl core::fmt::Display for Natural {
194 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
195 write!(f, "{}", self.symbol())
196 }
197}
198
199#[cfg(feature = "alloc")]
200impl ::core::str::FromStr for Flat {
201 type Err = crate::error::Error;
202
203 fn from_str(s: &str) -> Result<Self, Self::Err> {
204 if s.to_lowercase() == "flat" || s == "♭" || s == "b" {
205 Ok(Self::default())
206 } else {
207 Err(anyhow::anyhow!("Invalid accidental string: {}", s).into())
208 }
209 }
210}
211
212#[cfg(feature = "alloc")]
213impl ::core::str::FromStr for Sharp {
214 type Err = crate::error::Error;
215
216 fn from_str(s: &str) -> Result<Self, Self::Err> {
217 if s.to_lowercase() == "sharp" || s == "♯" || s == "#" {
218 Ok(Self::default())
219 } else {
220 Err(anyhow::anyhow!("Invalid accidental string: {}", s).into())
221 }
222 }
223}