1use std::fmt::{Display, Error, Formatter};
4
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7
8use pest::Parser;
9
10use crate::core::{
11 base::{HasDescription, HasName, HasPreciseName, HasStaticName, Parsable, Res},
12 chord::HasRoot,
13 interval::{HasIntervals, Interval},
14 mode_kind::ModeKind,
15 note::Note,
16 parser::{mode_name_str_to_mode_kind, note_str_to_note, ChordParser, Rule},
17};
18
19pub trait HasModeKind {
23 fn kind(&self) -> ModeKind;
25}
26
27#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34#[derive(PartialEq, Eq, Copy, Clone, Debug)]
35pub struct Mode {
36 root: Note,
38 kind: ModeKind,
40}
41
42impl Mode {
45 pub fn new(root: Note, kind: ModeKind) -> Self {
47 Self { root, kind }
48 }
49
50 pub fn intervals(&self) -> &'static [Interval] {
52 self.kind.intervals()
53 }
54
55 pub fn notes(&self) -> Vec<Note> {
57 self.intervals().iter().map(|&interval| self.root + interval).collect()
58 }
59}
60
61impl HasRoot for Mode {
62 fn root(&self) -> Note {
63 self.root
64 }
65}
66
67impl HasModeKind for Mode {
68 fn kind(&self) -> ModeKind {
69 self.kind
70 }
71}
72
73impl HasIntervals for Mode {
74 fn intervals(&self) -> &'static [Interval] {
75 self.kind.intervals()
76 }
77}
78
79impl HasStaticName for Mode {
80 fn static_name(&self) -> &'static str {
81 self.kind.static_name()
82 }
83}
84
85impl HasName for Mode {
86 fn name(&self) -> String {
87 format!("{} {}", self.root.static_name(), self.kind.static_name())
88 }
89}
90
91impl HasPreciseName for Mode {
92 fn precise_name(&self) -> String {
93 format!("{} {}", self.root.name(), self.kind.static_name())
94 }
95}
96
97impl HasDescription for Mode {
98 fn description(&self) -> &'static str {
99 self.kind.description()
100 }
101}
102
103impl Display for Mode {
104 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
105 let notes = self.notes().iter().map(|n| n.static_name()).collect::<Vec<_>>().join(", ");
106 write!(f, "{}\n {}\n {}", self.name(), self.description(), notes)
107 }
108}
109
110impl Parsable for Mode {
111 fn parse(input: &str) -> Res<Self>
112 where
113 Self: Sized,
114 {
115 let pairs = ChordParser::parse(Rule::mode, input)?;
116 let root = pairs.clone().next().unwrap();
117
118 assert_eq!(Rule::mode, root.as_rule());
119
120 let mut components = root.into_inner();
121
122 let note = components.next().unwrap();
123 assert_eq!(Rule::note_atomic, note.as_rule());
124 let root_note = note_str_to_note(note.as_str().trim())?;
125
126 let mode_name = components.next().unwrap();
127 assert_eq!(Rule::mode_name, mode_name.as_rule());
128 let mode_kind = mode_name_str_to_mode_kind(mode_name.as_str())?;
129
130 Ok(Mode::new(root_note, mode_kind))
131 }
132}
133
134#[cfg(test)]
137mod tests {
138 use super::*;
139 use crate::core::named_pitch::{HasLetter, HasNamedPitch};
140 use crate::core::note::*;
141 use pretty_assertions::assert_eq;
142
143 impl Mode {
144 pub(crate) fn validate_spelling(&self) -> Result<(), String> {
149 use crate::core::named_pitch::{HasLetter, HasNamedPitch};
150 use std::collections::HashMap;
151
152 let notes = self.notes();
153 let intervals_count = self.intervals().len();
154
155 if intervals_count == 12 {
157 return Ok(());
158 }
159
160 let mut letter_counts: HashMap<&str, usize> = HashMap::new();
162 for note in ¬es {
163 let letter = note.named_pitch().letter();
164 *letter_counts.entry(letter).or_insert(0) += 1;
165 }
166
167 if intervals_count == 7 {
169 if letter_counts.len() != 7 {
170 return Err(format!(
171 "{} {} has {} unique letters, expected 7. Letters: {:?}",
172 self.root().static_name(),
173 self.kind().static_name(),
174 letter_counts.len(),
175 notes.iter().map(|n| n.static_name()).collect::<Vec<_>>()
176 ));
177 }
178
179 for (letter, count) in &letter_counts {
180 if *count != 1 {
181 return Err(format!(
182 "{} {} has letter {} appearing {} times, expected 1. Notes: {:?}",
183 self.root().static_name(),
184 self.kind().static_name(),
185 letter,
186 count,
187 notes.iter().map(|n| n.static_name()).collect::<Vec<_>>()
188 ));
189 }
190 }
191 } else {
192 for (letter, count) in &letter_counts {
194 if *count > 1 {
195 return Err(format!(
196 "{} {} has letter {} appearing {} times. Notes: {:?}",
197 self.root().static_name(),
198 self.kind().static_name(),
199 letter,
200 count,
201 notes.iter().map(|n| n.static_name()).collect::<Vec<_>>()
202 ));
203 }
204 }
205 }
206
207 Ok(())
208 }
209 }
210
211 #[test]
212 fn test_mode_creation() {
213 let mode = Mode::new(D, ModeKind::Dorian);
214 assert_eq!(mode.root(), D);
215 assert_eq!(mode.kind(), ModeKind::Dorian);
216 }
217
218 #[test]
219 fn test_mode_intervals() {
220 let mode = Mode::new(D, ModeKind::Dorian);
221 assert_eq!(mode.intervals().len(), 7);
222 assert_eq!(
223 mode.intervals(),
224 &[
225 Interval::PerfectUnison,
226 Interval::MajorSecond,
227 Interval::MinorThird,
228 Interval::PerfectFourth,
229 Interval::PerfectFifth,
230 Interval::MajorSixth,
231 Interval::MinorSeventh,
232 ]
233 );
234 }
235
236 #[test]
237 fn test_mode_notes() {
238 let mode = Mode::new(D, ModeKind::Dorian);
240 assert_eq!(mode.notes(), vec![D, E, F, G, A, B, CFive]);
241
242 let mode = Mode::new(C, ModeKind::Ionian);
244 assert_eq!(mode.notes(), vec![C, D, E, F, G, A, B]);
245
246 let mode = Mode::new(E, ModeKind::Phrygian);
248 assert_eq!(mode.notes(), vec![E, F, G, A, B, CFive, DFive]);
249
250 let mode = Mode::new(F, ModeKind::Lydian);
252 assert_eq!(mode.notes(), vec![F, G, A, B, CFive, DFive, EFive]);
253
254 let mode = Mode::new(G, ModeKind::Mixolydian);
256 assert_eq!(mode.notes(), vec![G, A, B, CFive, DFive, EFive, FFive]);
257
258 let mode = Mode::new(A, ModeKind::Aeolian);
260 assert_eq!(mode.notes(), vec![A, B, CFive, DFive, EFive, FFive, GFive]);
261
262 let mode = Mode::new(B, ModeKind::Locrian);
264 assert_eq!(mode.notes(), vec![B, CFive, DFive, EFive, FFive, GFive, AFive]);
265 }
266
267 #[test]
268 fn test_mode_names() {
269 let mode = Mode::new(D, ModeKind::Dorian);
270 assert_eq!(mode.name(), "D dorian");
271 assert_eq!(mode.static_name(), "dorian");
272
273 let mode = Mode::new(FSharp, ModeKind::Lydian);
274 assert_eq!(mode.name(), "F♯ lydian");
275
276 let mode = Mode::new(BFlat, ModeKind::Mixolydian);
277 assert_eq!(mode.name(), "B♭ mixolydian");
278 }
279
280 #[test]
281 fn test_mode_display() {
282 let mode = Mode::new(D, ModeKind::Dorian);
283 let display = format!("{}", mode);
284 assert!(display.contains("D dorian"));
285 assert!(display.contains("D, E, F, G, A, B, C"));
286 assert!(display.contains("dorian"));
287 }
288
289 #[test]
290 fn test_all_modes_of_c_major() {
291 let c_ionian = Mode::new(C, ModeKind::Ionian);
295 assert_eq!(c_ionian.notes(), vec![C, D, E, F, G, A, B]);
296
297 let d_dorian = Mode::new(D, ModeKind::Dorian);
298 assert_eq!(d_dorian.notes(), vec![D, E, F, G, A, B, CFive]);
299
300 let e_phrygian = Mode::new(E, ModeKind::Phrygian);
301 assert_eq!(e_phrygian.notes(), vec![E, F, G, A, B, CFive, DFive]);
302
303 let f_lydian = Mode::new(F, ModeKind::Lydian);
304 assert_eq!(f_lydian.notes(), vec![F, G, A, B, CFive, DFive, EFive]);
305
306 let g_mixolydian = Mode::new(G, ModeKind::Mixolydian);
307 assert_eq!(g_mixolydian.notes(), vec![G, A, B, CFive, DFive, EFive, FFive]);
308
309 let a_aeolian = Mode::new(A, ModeKind::Aeolian);
310 assert_eq!(a_aeolian.notes(), vec![A, B, CFive, DFive, EFive, FFive, GFive]);
311
312 let b_locrian = Mode::new(B, ModeKind::Locrian);
313 assert_eq!(b_locrian.notes(), vec![B, CFive, DFive, EFive, FFive, GFive, AFive]);
314 }
315
316 #[test]
317 fn test_mode_characteristic_intervals() {
318 let mode = Mode::new(D, ModeKind::Dorian);
320 let notes = mode.notes();
321 assert_eq!(notes[5], B); let mode = Mode::new(E, ModeKind::Phrygian);
325 let notes = mode.notes();
326 assert_eq!(notes[1], F); let mode = Mode::new(F, ModeKind::Lydian);
330 let notes = mode.notes();
331 assert_eq!(notes[3], B); let mode = Mode::new(B, ModeKind::Locrian);
335 let notes = mode.notes();
336 assert_eq!(notes[4], FFive); }
338
339 #[test]
340 fn test_mode_parse() {
341 let mode = Mode::parse("D dorian").unwrap();
343 assert_eq!(mode.root(), D);
344 assert_eq!(mode.kind(), ModeKind::Dorian);
345
346 let mode = Mode::parse("C ionian").unwrap();
347 assert_eq!(mode.root(), C);
348 assert_eq!(mode.kind(), ModeKind::Ionian);
349
350 let mode = Mode::parse("E phrygian").unwrap();
351 assert_eq!(mode.root(), E);
352 assert_eq!(mode.kind(), ModeKind::Phrygian);
353
354 let mode = Mode::parse("F lydian").unwrap();
355 assert_eq!(mode.root(), F);
356 assert_eq!(mode.kind(), ModeKind::Lydian);
357
358 let mode = Mode::parse("G mixolydian").unwrap();
359 assert_eq!(mode.root(), G);
360 assert_eq!(mode.kind(), ModeKind::Mixolydian);
361
362 let mode = Mode::parse("A aeolian").unwrap();
363 assert_eq!(mode.root(), A);
364 assert_eq!(mode.kind(), ModeKind::Aeolian);
365
366 let mode = Mode::parse("B locrian").unwrap();
367 assert_eq!(mode.root(), B);
368 assert_eq!(mode.kind(), ModeKind::Locrian);
369
370 let mode = Mode::parse("F# dorian").unwrap();
372 assert_eq!(mode.root(), FSharp);
373 assert_eq!(mode.kind(), ModeKind::Dorian);
374
375 let mode = Mode::parse("Bb lydian").unwrap();
376 assert_eq!(mode.root(), BFlat);
377 assert_eq!(mode.kind(), ModeKind::Lydian);
378 }
379
380 #[test]
381 fn test_harmonic_minor_modes_parse() {
382 let mode = Mode::parse("B locrian nat6").unwrap();
384 assert_eq!(mode.kind(), ModeKind::LocrianNatural6);
385
386 let mode = Mode::parse("C ionian #5").unwrap();
387 assert_eq!(mode.kind(), ModeKind::IonianSharp5);
388
389 let mode = Mode::parse("D dorian sharp 4").unwrap();
390 assert_eq!(mode.kind(), ModeKind::DorianSharp4);
391
392 let mode = Mode::parse("E phrygian dominant").unwrap();
393 assert_eq!(mode.kind(), ModeKind::PhrygianDominant);
394
395 let mode = Mode::parse("F lydian #2").unwrap();
396 assert_eq!(mode.kind(), ModeKind::LydianSharp2);
397
398 let mode = Mode::parse("G# ultralocrian").unwrap();
399 assert_eq!(mode.kind(), ModeKind::Ultralocrian);
400 }
401
402 #[test]
403 fn test_melodic_minor_modes_parse() {
404 let mode = Mode::parse("B dorian b2").unwrap();
406 assert_eq!(mode.kind(), ModeKind::DorianFlat2);
407
408 let mode = Mode::parse("C lydian augmented").unwrap();
409 assert_eq!(mode.kind(), ModeKind::LydianAugmented);
410
411 let mode = Mode::parse("D lydian dominant").unwrap();
412 assert_eq!(mode.kind(), ModeKind::LydianDominant);
413
414 let mode = Mode::parse("E mixolydian b6").unwrap();
415 assert_eq!(mode.kind(), ModeKind::MixolydianFlat6);
416
417 let mode = Mode::parse("F# locrian nat2").unwrap();
418 assert_eq!(mode.kind(), ModeKind::LocrianNatural2);
419
420 let mode = Mode::parse("G# altered").unwrap();
421 assert_eq!(mode.kind(), ModeKind::Altered);
422 }
423
424 #[test]
425 fn test_enharmonic_spelling_diatonic_modes() {
426 let mode = Mode::new(C, ModeKind::Ionian);
431 mode.validate_spelling().unwrap();
432
433 let mode = Mode::new(FSharp, ModeKind::Dorian);
435 mode.validate_spelling().unwrap();
436 let notes = mode.notes();
437 assert_eq!(notes[0].named_pitch().letter(), "F");
439 assert_eq!(notes[1].named_pitch().letter(), "G");
440 assert_eq!(notes[2].named_pitch().letter(), "A");
441 assert_eq!(notes[3].named_pitch().letter(), "B");
442 assert_eq!(notes[4].named_pitch().letter(), "C");
443 assert_eq!(notes[5].named_pitch().letter(), "D");
444 assert_eq!(notes[6].named_pitch().letter(), "E");
445
446 let mode = Mode::new(DFlat, ModeKind::Lydian);
448 mode.validate_spelling().unwrap();
449 let notes = mode.notes();
450 assert_eq!(notes[0].named_pitch().letter(), "D");
452 assert_eq!(notes[1].named_pitch().letter(), "E");
453 assert_eq!(notes[2].named_pitch().letter(), "F");
454 assert_eq!(notes[3].named_pitch().letter(), "G");
455 assert_eq!(notes[4].named_pitch().letter(), "A");
456 assert_eq!(notes[5].named_pitch().letter(), "B");
457 assert_eq!(notes[6].named_pitch().letter(), "C");
458
459 let mode = Mode::new(BFlat, ModeKind::Mixolydian);
461 mode.validate_spelling().unwrap();
462
463 let mode = Mode::new(E, ModeKind::Locrian);
465 mode.validate_spelling().unwrap();
466 }
467
468 #[test]
469 fn test_enharmonic_spelling_harmonic_minor_modes() {
470 let mode = Mode::new(FSharp, ModeKind::LocrianNatural6);
474 mode.validate_spelling().unwrap();
475
476 let mode = Mode::new(C, ModeKind::IonianSharp5);
478 mode.validate_spelling().unwrap();
479
480 let mode = Mode::new(D, ModeKind::DorianSharp4);
482 mode.validate_spelling().unwrap();
483
484 let mode = Mode::new(E, ModeKind::PhrygianDominant);
486 mode.validate_spelling().unwrap();
487
488 let mode = Mode::new(F, ModeKind::LydianSharp2);
490 mode.validate_spelling().unwrap();
491
492 let mode = Mode::new(GSharp, ModeKind::Ultralocrian);
494 mode.validate_spelling().unwrap();
495 }
496
497 #[test]
498 fn test_enharmonic_spelling_melodic_minor_modes() {
499 let mode = Mode::new(B, ModeKind::DorianFlat2);
503 mode.validate_spelling().unwrap();
504
505 let mode = Mode::new(C, ModeKind::LydianAugmented);
507 mode.validate_spelling().unwrap();
508
509 let mode = Mode::new(D, ModeKind::LydianDominant);
511 mode.validate_spelling().unwrap();
512
513 let mode = Mode::new(E, ModeKind::MixolydianFlat6);
515 mode.validate_spelling().unwrap();
516
517 let mode = Mode::new(FSharp, ModeKind::LocrianNatural2);
519 mode.validate_spelling().unwrap();
520
521 let mode = Mode::new(GSharp, ModeKind::Altered);
523 mode.validate_spelling().unwrap();
524 }
525
526 #[test]
527 fn test_enharmonic_spelling_all_roots() {
528 for root in [C, CSharp, D, DFlat, DSharp, E, EFlat, F, FSharp, G, GFlat, GSharp, A, AFlat, ASharp, B, BFlat] {
530 let mode = Mode::new(root, ModeKind::Ionian);
532 mode.validate_spelling().unwrap_or_else(|e| panic!("Ionian spelling failed for {}: {}", root.static_name(), e));
533
534 let mode = Mode::new(root, ModeKind::Dorian);
536 mode.validate_spelling().unwrap_or_else(|e| panic!("Dorian spelling failed for {}: {}", root.static_name(), e));
537
538 let mode = Mode::new(root, ModeKind::Lydian);
540 mode.validate_spelling().unwrap_or_else(|e| panic!("Lydian spelling failed for {}: {}", root.static_name(), e));
541 }
542 }
543
544 #[test]
545 fn test_mode_spelling() {
546 let mode = Mode::new(E, ModeKind::PhrygianDominant);
547 assert_eq!(mode.notes(), vec![E, F, GSharp, A, B, CFive, DFive], "E phrygian dominant spelling incorrect");
548 mode.validate_spelling().unwrap();
549
550 let mode = Mode::new(B, ModeKind::LocrianNatural6);
551 assert_eq!(mode.notes(), vec![B, CFive, DFive, EFive, FFive, GSharpFive, AFive], "B locrian nat6 spelling incorrect");
552 mode.validate_spelling().unwrap();
553
554 let mode = Mode::new(D, ModeKind::LydianDominant);
555 assert_eq!(mode.notes(), vec![D, E, FSharp, GSharp, A, B, CFive], "D lydian dominant spelling incorrect");
556 mode.validate_spelling().unwrap();
557
558 assert_eq!(
559 mode.intervals(),
560 &[
561 Interval::PerfectUnison,
562 Interval::MajorSecond,
563 Interval::MajorThird,
564 Interval::AugmentedFourth,
565 Interval::PerfectFifth,
566 Interval::MajorSixth,
567 Interval::MinorSeventh,
568 ]
569 );
570 }
571}