hapsi 0.0.3

A music theory package
Documentation
use std::collections::HashMap;

use once_cell::sync::Lazy;

use crate::core::{Cycle, Octave};

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Tone {
    tone: ToneSymbol,
    accidental: AccidentalSymbol,
}

impl Tone {
    pub fn new(tone: ToneSymbol, accidental: AccidentalSymbol) -> Self {
        Tone { tone, accidental }.normalize()
    }

    pub fn tone(&self) -> &ToneSymbol {
        &self.tone
    }

    pub fn accidental(&self) -> &AccidentalSymbol {
        &self.accidental
    }

    fn normalize(self) -> Self {
        use AccidentalSymbol::*;
        use ToneSymbol::*;
        match (&self.tone, &self.accidental) {
            (&C, &Flat) => Self::new(B, Natural),
            (&E, &Sharp) => Self::new(F, Natural),
            (&F, &Flat) => Self::new(E, Natural),
            (&B, &Sharp) => Self::new(C, Natural),
            (_, _) => self,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(i32)]
pub enum ToneSymbol {
    C = 0,
    D = 2,
    E = 4,
    F = 5,
    G = 7,
    A = 9,
    B = 11,
}

impl Default for ToneSymbol {
    fn default() -> Self {
        Self::C
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(i32)]
pub enum AccidentalSymbol {
    Flat = -1,
    Natural = 0,
    Sharp = 1,
}

impl Default for AccidentalSymbol {
    fn default() -> Self {
        Self::Natural
    }
}

impl From<Tone> for usize {
    fn from(value: Tone) -> Self {
        (value.tone as i32 + value.accidental as i32) as usize
    }
}

static TWELVE_MAP: Lazy<HashMap<usize, Tone>> = Lazy::new(|| {
    use AccidentalSymbol::*;
    use ToneSymbol::*;
    let mut map = HashMap::<usize, Tone>::new();
    map.insert(0, Tone::new(C, Natural));
    map.insert(1, Tone::new(C, Sharp));
    map.insert(2, Tone::new(D, Natural));
    map.insert(3, Tone::new(D, Sharp));
    map.insert(4, Tone::new(E, Natural));
    map.insert(5, Tone::new(F, Natural));
    map.insert(6, Tone::new(F, Sharp));
    map.insert(7, Tone::new(G, Natural));
    map.insert(8, Tone::new(G, Sharp));
    map.insert(9, Tone::new(A, Natural));
    map.insert(10, Tone::new(A, Sharp));
    map.insert(11, Tone::new(B, Natural));
    map
});

#[derive(Debug)]
pub struct Twelve;

impl Octave for Twelve {
    type PitchClass = Tone;

    fn get_class(&self, number: &Cycle) -> &Self::PitchClass {
        TWELVE_MAP.get_class(number)
    }

    fn get_number(&self, class: &Self::PitchClass) -> Option<usize> {
        TWELVE_MAP.get_number(class)
    }

    fn len(&self) -> usize {
        12
    }
}