rust_music_theory/scale/
mode.rs

1use crate::scale::errors::ScaleError;
2use crate::scale::errors::ScaleError::ModeFromRegex;
3use crate::scale::mode::Mode::*;
4use lazy_static::lazy_static;
5use regex::{Match, Regex};
6use strum_macros::{Display, EnumIter};
7
8lazy_static! {
9    static ref MODE_REGEXES: Vec<(Regex, Mode)> = vec![
10        (
11            Regex::new(r"^(M\s+|M$|(?i)maj|major|ionian)").unwrap(),
12            Ionian
13        ),
14        (
15            Regex::new(r"(?i)(har minor|harmonicminor|harmonic\s+minor)").unwrap(),
16            HarmonicMinor
17        ),
18        (
19            Regex::new(r"(?i)(mel minor|melodicminor|melodic\s+minor)").unwrap(),
20            MelodicMinor
21        ),
22        (
23            Regex::new(r"(?i)(pentatonic\s+major|pentatonic\s+maj|pent\s+maj|pent\s+major)").unwrap(),
24            PentatonicMajor
25        ),
26        (
27            Regex::new(r"(?i)(pentatonic\s+minor|pentatonic\s+min|pent\s+min|pent\s+minor)").unwrap(),
28            PentatonicMinor
29        ),
30        (
31            Regex::new(r"(?i)(blues)").unwrap(),
32            Blues
33        ),
34        (
35            Regex::new(r"(?i)(chromatic)").unwrap(),
36            Chromatic
37        ),
38        (
39            Regex::new(r"(?i)(whole\s+tone|wholetone)").unwrap(),
40            WholeTone
41        ),
42        (
43            Regex::new(r"^(m\s+|m$|(?i)min|minor|aeolian)").unwrap(),
44            Aeolian
45        ),
46        (Regex::new(r"(?i)^(dorian)").unwrap(), Dorian),
47        (Regex::new(r"(?i)^(locrian)").unwrap(), Locrian),
48        (Regex::new(r"(?i)^(mixolydian)").unwrap(), Mixolydian),
49        (Regex::new(r"(?i)^(phrygian)").unwrap(), Phrygian),
50        (Regex::new(r"(?i)^(lydian)").unwrap(), Lydian),
51    ];
52}
53
54/// The mode of a scale.
55#[derive(Display, Debug, Clone, Copy, EnumIter, PartialEq, Eq)]
56pub enum Mode {
57    /// Also known as a major scale.
58    Ionian,
59    Dorian,
60    Phrygian,
61    Lydian,
62    Mixolydian,
63    /// Also known as a natural minor scale.
64    Aeolian,
65    Locrian,
66    HarmonicMinor,
67    MelodicMinor,
68    PentatonicMajor,
69    PentatonicMinor,
70    Blues,
71    Chromatic,
72    WholeTone,
73}
74
75impl Mode {
76    /// Parse a mode using a regex.
77    pub fn from_regex(string: &str) -> Result<(Self, Match), ScaleError> {
78        for (regex, mode_enum) in &*MODE_REGEXES {
79            let mode = regex.find(string.trim());
80
81            if let Some(mode_match) = mode {
82                return Ok((*mode_enum, mode_match));
83            };
84        }
85
86        Err(ModeFromRegex)
87    }
88
89    /// Get whether the mode is diatonic (not harmonic or melodic minor).
90    pub fn is_diatonic(self) -> bool {
91        !matches!(self, Self::HarmonicMinor | Self::MelodicMinor | 
92                      Self::PentatonicMajor | Self::PentatonicMinor | 
93                      Self::Blues | Self::Chromatic | Self::WholeTone)
94    }
95}