rstmt_core/pitch/
pitch_class.rs

1/*
2    Appellation: pitch_class <module>
3    Created At: 2025.12.20:09:31:05
4    Contrib: @FL03
5*/
6use crate::pitch::{Accidental, PitchClassRepr, RawPitchClass};
7use rstmt_traits::PitchMod;
8
9pub trait IntoPitchClass<P, K>
10where
11    P: RawPitchClass<Tag = K>,
12    K: Accidental,
13{
14    fn into_pitch_class(self) -> PitchClass<P, K>;
15
16    private! {}
17}
18
19/// The [`PitchClass`] implementations works to generically define the structure for a pitch
20/// class. This is accomplished through the use of two type parameters: `N`, which defines the
21/// note (e.g., C, D, E, etc.), and `K`, which defines the kind of pitch (e.g., sharp, flat,
22/// natural, etc.).
23///
24/// **Note**: This struct isn't designed to be used directly, rather through type aliases such
25/// as [`C`], [`DSharp`], [`EFlat`], etc.
26#[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
27#[cfg_attr(
28    feature = "serde",
29    derive(serde::Deserialize, serde::Serialize),
30    serde(rename_all = "lowercase")
31)]
32#[repr(C)]
33pub struct PitchClass<P = super::CNote, K = <P as RawPitchClass>::Tag>
34where
35    P: RawPitchClass<Tag = K>,
36    K: Accidental,
37{
38    pub(crate) class: P,
39    pub(crate) kind: K,
40}
41
42/*
43 ************* Types *************
44*/
45macro_rules! classes {
46    (@impl $name:ident::<Natural>) => {
47        paste::paste! {
48            pub type $name = $crate::pitch::PitchClass<$crate::pitch::[<$name Note>], $crate::pitch::Natural>;
49        }
50    };
51    (@impl $name:ident::<$kind:ident>) => {
52        paste::paste! {
53            pub type [<$name $kind>] = $crate::pitch::PitchClass<$crate::pitch::[<$name $kind Note>], $crate::pitch::$kind>;
54        }
55    };
56    (@impl $name:ident::<$($kind:ident),+ $(,)?>) => {
57        $(classes! { @impl $name::<$kind> })*
58    };
59    ($($name:ident::<$($K:ident),* $(,)?>),* $(,)?) => {
60        $(classes! { @impl $name::<Natural, $($K),*> })*
61    };
62}
63
64classes! {
65    C::<Sharp>,
66    D::<Flat, Sharp>,
67    E::<Flat>,
68    F::<Sharp>,
69    G::<Flat, Sharp>,
70    A::<Flat, Sharp>,
71    B::<Flat>,
72}
73
74impl<P, K> IntoPitchClass<P, K> for isize
75where
76    P: PitchClassRepr<Tag = K>,
77    K: Accidental,
78{
79    seal! {}
80
81    fn into_pitch_class(self) -> PitchClass<P, K> {
82        match self.pmod() {
83            x if x == P::IDX => PitchClass::new(),
84            _ => panic!("cannot convert {self} into pitch class"),
85        }
86    }
87}