1pub mod chord;
30pub mod utils;
32
33use crate::chord::*;
34use crate::utils::*;
35
36#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
38pub enum ChordParseErrorKind {
39 InvalidRoot,
41 InvalidAlterations,
43 Unknown, }
46
47pub enum ChordParseResult {
49 Success(Chord),
51 Failure(ChordParseErrorKind),
53}
54
55pub struct ChordParser {
57 reader: TextReader,
58}
59
60impl ChordParser {
61 pub fn new() -> Self {
64 ChordParser { reader: TextReader::new() }
65 }
66
67 pub fn from_str(s: &str) -> ChordParseResult {
82 ChordParser::new().parse(s)
83 }
84
85 fn normalize_input(&self, s: &str) -> String {
86 s.chars().filter(|c| !c.is_whitespace()).collect()
87 }
88
89 fn new_reader(&mut self, s: &str) {
90 let normalized = self.normalize_input(s);
91 self.reader = TextReader::from_text(normalized.as_str());
92 }
93
94 fn try_read_number(&mut self) -> Option<usize> {
95 let mut str = String::new();
96
97 while self.reader.is_end() == false {
98 let ch = self.reader.next().unwrap();
99
100 if ch.is_ascii_digit() {
101 str.push(ch);
102 continue;
103 }
104
105 self.reader.rollback(1).unwrap();
106 break;
107 }
108
109 if str.is_empty() || str.len() > 2 { self.reader.rollback(str.len()).unwrap();
111 return None;
112 }
113
114 Some(str.parse().unwrap())
115 }
116
117 fn try_read_interval(&mut self) -> Option<AlteredInterval> {
118 let num = match self.try_read_number() {
119 Some(num) => num,
120 None => return None,
121 };
122
123 match AlteredInterval::from_usize(num) {
124 Some(interval) => Some(interval),
125 None => {
126 if num > 9 {
127 self.reader.rollback(2).unwrap();
128 } else {
129 self.reader.rollback(1).unwrap();
130 }
131
132 None
133 }
134 }
135 }
136
137 fn try_read_alter_accidental(&mut self) -> Option<Accidental> {
138 if self.reader.is_end() {
139 return None;
140 }
141
142 let fst = self.reader.next().unwrap();
143
144 if fst == '+' {
145 return Some(Accidental::Sharp);
146 } else if fst == '-' {
147 return Some(Accidental::Flat);
148 }
149
150 let mut s = String::with_capacity(2);
151 s.push(fst);
152
153 if let Some(acc) = Accidental::from_str(s.as_str()) {
154 if self.reader.is_end() == false {
155 s.push(self.reader.next().unwrap());
156
157 if let Some(acc) = Accidental::from_str(s.as_str()) { return Some(acc);
159 }
160
161 self.reader.rollback(1).unwrap();
162 }
163
164 return Some(acc);
165 }
166
167 self.reader.rollback(1).unwrap();
168
169 None
170 }
171
172 fn try_read_note_alter(&mut self, require_accidental: bool) -> Option<ChordNoteAlter> {
173 let accidental = match self.try_read_alter_accidental() {
174 Some(accidental) => accidental,
175 None => {
176 if require_accidental {
177 return None;
178 }
179
180 Accidental::Natural
181 }
182 };
183
184 let interval = match self.try_read_interval() {
185 Some(interval) => interval,
186 None => return None,
187 };
188
189 Some(ChordNoteAlter { accidental, interval })
190 }
191
192 fn try_read_note(&mut self) -> Option<Note> {
193 let note = match self.reader.next() {
194 Some(note) => note,
195 None => return None,
196 };
197
198 let pitch = match Pitch::from_char(¬e) {
199 Some(pitch) => pitch,
200 None => {
201 self.reader.rollback(1).unwrap();
202 return None;
203 }
204 };
205
206 let accidental = match self.reader.is_end() {
207 true => Accidental::Natural,
208 false => {
209 let fst = self.reader.next().unwrap();
210
211 if let Some(res) = Accidental::from_str(&fst.to_string()) {
212 if self.reader.is_end() {
213 res
214 } else {
215 let snd = self.reader.next().unwrap();
216 let mut full = String::new();
217
218 full.push(fst);
219 full.push(snd);
220
221 if let Some(res) = Accidental::from_str(&full) {
222 res
223 } else {
224 self.reader.rollback(1).unwrap();
225
226 res
227 }
228 }
229 } else {
230 self.reader.rollback(1).unwrap();
231
232 Accidental::Natural
233 }
234 }
235 };
236
237 Some(Note { pitch, accidental })
238 }
239
240 fn is_one_of(&mut self, elems: &mut Vec<&str>, insensitive: bool) -> bool {
241 elems.sort_by(|&a, &b| a.chars().count().cmp(&b.chars().count()));
242 elems.reverse(); for &el in elems.iter() {
246 if let Some(s) = self.reader.try_read(el.chars().count()) {
247 if insensitive && el.to_lowercase() == s.to_lowercase() {
248 return true;
249 } else if insensitive == false && el == s {
250 return true;
251 } else {
252 self.reader.rollback(el.chars().count()).unwrap();
253 }
254 }
255 }
256
257 false
258 }
259
260 fn parse_alterations(&mut self) -> Option<Alterations> {
261 let mut alters = Alterations::new();
262
263 if self.is_one_of(&mut vec!["Δ", "ma", "maj"], true)
277 || self.is_one_of(&mut vec!["M"], false) {
278 alters.seventh = Seventh::Major;
279 }
280
281 if self.is_one_of(&mut vec!["dom"], true) {
282 alters.seventh = Seventh::Flat;
283 }
284
285 if let Some(interval) = self.try_read_interval() {
286 if alters.seventh == Seventh::None {
287 alters.seventh = Seventh::Flat;
288 }
289
290 match interval {
291 AlteredInterval::Second => alters.set_note(&ChordNoteAlter
292 { interval: AlteredInterval::Second,
293 accidental: Accidental::Natural }),
294 AlteredInterval::Fifth => alters.no = No::Third, AlteredInterval::Sixth => {
296 alters.seventh = Seventh::None;
297 alters.set_note(&ChordNoteAlter {
298 interval: AlteredInterval::Sixth,
299 accidental: Accidental::Natural
300 });
301 }
302 AlteredInterval::Seventh => (),
303 AlteredInterval::Ninth =>
304 alters.set_note(&ChordNoteAlter
305 { interval: AlteredInterval::Ninth,
306 accidental: Accidental::Natural }),
307 AlteredInterval::Eleventh => {
308 alters.set_suspension(&AlteredInterval::Fourth);
309 }
310 AlteredInterval::Thirteenth => {
311 alters.set_note(&ChordNoteAlter
312 { interval: AlteredInterval::Ninth,
313 accidental: Accidental::Natural });
314 alters.set_note(&ChordNoteAlter
315 { interval: AlteredInterval::Thirteenth,
316 accidental: Accidental::Natural });
317 }
318 _ => return None,
319 }
320 }
321
322 while self.reader.is_end() == false {
323 if let Some(s) = self.reader.try_read(3) {
324 if s.to_lowercase() == "add" {
325 let alter = match self.try_read_note_alter(false) {
326 Some(alter) => alter,
327 None => return None,
328 };
329
330 if let Some(_) = alters.get_note(&alter.interval) { return None;
332 }
333
334 alters.set_note(&alter);
335 continue;
336 } else if s.to_lowercase() == "sus" {
337 if let Some(_) = alters.get_suspension() { return None;
339 }
340 if self.reader.is_end() {
341 alters.set_suspension(&AlteredInterval::Fourth);
342 break;
343 }
344
345 let interval = self.try_read_interval();
346
347 match interval {
348 Some(interval) => {
349 if interval == AlteredInterval::Thirteenth {
350 alters.seventh = Seventh::Flat;
351
352 alters.set_note(&ChordNoteAlter
353 { interval: AlteredInterval::Ninth,
354 accidental: Accidental::Natural });
355 alters.set_note(&ChordNoteAlter
356 { interval: AlteredInterval::Thirteenth,
357 accidental: Accidental::Natural });
358
359 alters.set_suspension(&AlteredInterval::Fourth);
360 break;
361 }
362
363 alters.set_suspension(&interval);
364 }
365 None => return None,
366 }
367
368 continue;
369 }
370
371 self.reader.rollback(3).unwrap();
372 }
373
374 if self.is_one_of(&mut vec!["no", "no."], true) {
375 if alters.no != No::None { return None;
377 }
378
379 let interval = match self.try_read_number() {
380 Some(num) => match num {
381 3 => No::Third,
382 5 => No::Fifth,
383 _ => return None,
384 },
385 None => return None,
386 };
387
388 alters.no = interval;
389 continue;
390 }
391
392 if let Some(alter) = self.try_read_note_alter(true) {
393 if let Some(_) = alters.get_note(&alter.interval) {
394 return None;
395 }
396
397 alters.set_note(&alter);
398 continue;
399 }
400
401 let ch = self.reader.next().unwrap();
402
403 if ch == '(' { while self.reader.is_end() == false {
405 if self.is_one_of(&mut vec!["ma", "maj"], true) ||
406 self.is_one_of(&mut vec!["Δ", "M"], false) { if self.reader.is_end() || self.reader.next().unwrap() != '7' {
408 return None;
409 }
410 if alters.seventh != Seventh::None {
411 return None; }
413
414 alters.seventh = Seventh::Major;
415 } else if self.is_one_of(&mut vec!["no", "no."], true) {
416 let interval = match self.try_read_number() {
417 Some(num) => match num {
418 3 => No::Third,
419 5 => No::Fifth,
420 _ => return None,
421 },
422 None => return None,
423 };
424
425 if alters.no != No::None { return None;
427 }
428
429 alters.no = interval;
430 } else {
431 let alter = match self.try_read_note_alter(false) {
432 Some(alter) => alter,
433 None => return None,
434 };
435
436 if alter.interval == AlteredInterval::Seventh {
437 alters.seventh = Seventh::Flat;
438 } else {
439 if let Some(_) = alters.get_note(&alter.interval) { return None;
441 }
442
443 alters.set_note(&alter);
444 }
445 }
446
447 let next_char = match self.reader.next() {
448 Some(ch) => ch,
449 None => return None,
450 };
451
452 if next_char == ')' {
453 break;
454 }
455 if next_char != ',' {
456 return None;
457 }
458 }
459
460 continue;
461 } else if ch == '/' || ch == '\\' {
462 let alter = self.try_read_note_alter(false);
463
464 if let Some(alter) = alter {
465 if let Some(_) = alters.get_note(&alter.interval) { return None;
467 }
468
469 alters.set_note(&alter);
470 continue;
471 }
472
473 let note = match self.try_read_note() {
475 Some(note) => note,
476 None => return None,
477 };
478
479 if alters.slash != None { return None;
481 }
482
483 alters.slash = Some(note);
484 continue;
485 }
486
487 return None;
488 }
489
490 Some(alters)
491 }
492
493 fn parse_chord_type(&mut self) -> ChordTriadType {
494 if self.reader.is_end() {
501 return ChordTriadType::Major;
502 }
503
504 let partial_maj = "ma";
505 let partial_min = "mi";
506
507 let full_maj = "maj";
508 let full_min = "min";
509 let full_aug = "aug";
510 let full_dim = "dim"; if let Some(s) = self.reader.try_read(full_maj.len()) {
513 if s.to_lowercase() == full_maj {
514 if self.reader.is_end() == false {
515 self.reader.rollback(full_maj.len()).unwrap();
516 }
517
518 return ChordTriadType::Major;
519 }
520 if s.to_lowercase().get(..partial_maj.len()).unwrap() == partial_maj {
521 self.reader.rollback(full_maj.len()).unwrap();
522 return ChordTriadType::Major;
523 }
524 if s.to_lowercase() == full_min {
525 return ChordTriadType::Minor;
526 }
527 if s.to_lowercase().get(..partial_min.len()).unwrap() == partial_min {
528 self.reader.rollback(full_min.len() - partial_min.len()).unwrap();
529 return ChordTriadType::Minor;
530 }
531 if s.to_lowercase() == full_aug {
532 return ChordTriadType::Augmented;
533 }
534 if s.to_lowercase() == full_dim {
535 return ChordTriadType::Diminished;
536 }
537
538 self.reader.rollback(full_maj.len()).unwrap();
539 }
540
541 if let Some(s) = self.reader.try_read(partial_maj.len()) {
542 if s.to_lowercase() == partial_maj {
543 if self.reader.is_end() == false {
544 self.reader.rollback(partial_maj.len()).unwrap();
545 }
546
547 return ChordTriadType::Major;
548 }
549
550 self.reader.rollback(partial_maj.len()).unwrap();
551 }
552
553 let fst = self.reader.next().unwrap();
554
555 if fst == '+' {
556 return ChordTriadType::Augmented;
557 }
558 if fst == '-' {
559 return ChordTriadType::Minor;
560 }
561 if fst == 'm' {
562 return ChordTriadType::Minor;
563 }
564 if fst == 'M' {
565 if self.reader.is_end() {
566 return ChordTriadType::Major;
567 }
568 }
569 if fst == '°' || fst == 'o' {
570 return ChordTriadType::Diminished;
571 }
572
573 self.reader.rollback(1).unwrap();
574
575 ChordTriadType::Major
576 }
577
578 pub fn parse(&mut self, s: &str) -> ChordParseResult {
594 self.new_reader(s);
595
596 let note = match self.try_read_note() {
597 Some(note) => note,
598 None => return ChordParseResult::Failure(ChordParseErrorKind::InvalidRoot),
599 };
600
601 let chord_type = self.parse_chord_type();
602
603 let alterations = match self.parse_alterations() {
604 Some(alterations) => alterations,
605 None => return ChordParseResult::Failure(ChordParseErrorKind::InvalidAlterations),
606 };
607
608 ChordParseResult::Success(Chord {
609 note,
610 chord_type,
611 alterations,
612 })
613 }
614
615 pub fn try_parse_interval(&mut self, s: &str) -> Option<AlteredInterval> {
631 self.new_reader(s);
632
633 self.try_read_interval()
634 }
635
636 pub fn try_parse_note_alter(&mut self, s: &str, require_accidental: bool) -> Option<ChordNoteAlter> {
652 self.new_reader(s);
653
654 self.try_read_note_alter(require_accidental)
655 }
656}
657
658#[cfg(test)]
659mod tests {
660 use super::*;
661
662 #[test]
663 fn number_parsing() {
664 let mut parser = ChordParser::new();
665
666 parser.new_reader("15");
667 assert_eq!(Some(15), parser.try_read_number());
668 parser.new_reader("12asf");
669 assert_eq!(Some(12), parser.try_read_number());
670
671 parser.new_reader("asghjka");
672 assert_eq!(None, parser.try_read_number());
673 parser.new_reader("");
674 assert_eq!(None, parser.try_read_number());
675 parser.new_reader("123"); assert_eq!(None, parser.try_read_number());
677 }
678
679 #[test]
680 fn alter_interval_parsing() {
681 let mut parser = ChordParser::new();
682
683 parser.new_reader("13");
684 assert_eq!(Some(AlteredInterval::Thirteenth), parser.try_read_interval());
685 parser.new_reader("9");
686 assert_eq!(Some(AlteredInterval::Ninth), parser.try_read_interval());
687 parser.new_reader("2");
688 assert_eq!(Some(AlteredInterval::Second), parser.try_read_interval());
689
690 parser.new_reader("15");
691 assert_eq!(None, parser.try_read_interval());
692 parser.new_reader("");
693 assert_eq!(None, parser.try_read_interval());
694 parser.new_reader("1326");
695 assert_eq!(None, parser.try_read_interval());
696 }
697
698 #[test]
699 fn note_alteration_parsing() {
700 let mut parser = ChordParser::new();
701
702 parser.new_reader("+9");
703 assert_eq!(
704 ChordNoteAlter { accidental: Accidental::Sharp, interval: AlteredInterval::Ninth },
705 parser.try_read_note_alter(true).unwrap()
706 );
707 parser.new_reader("#9");
708 assert_eq!(
709 ChordNoteAlter { accidental: Accidental::Sharp, interval: AlteredInterval::Ninth },
710 parser.try_read_note_alter(true).unwrap()
711 );
712 parser.new_reader("##9");
713 assert_eq!(
714 ChordNoteAlter { accidental: Accidental::DoubleSharp, interval: AlteredInterval::Ninth },
715 parser.try_read_note_alter(true).unwrap()
716 );
717 parser.new_reader("bB9");
718 assert_eq!(
719 ChordNoteAlter { accidental: Accidental::DoubleFlat, interval: AlteredInterval::Ninth },
720 parser.try_read_note_alter(true).unwrap()
721 );
722
723 parser.new_reader("b15");
724 assert_eq!(None, parser.try_read_note_alter(true));
725 parser.new_reader("####13");
726 assert_eq!(None, parser.try_read_note_alter(true));
727 parser.new_reader("13");
728 assert_eq!(None, parser.try_read_note_alter(true));
729 parser.new_reader("b");
730 assert_eq!(None, parser.try_read_note_alter(true));
731 parser.new_reader("gibberish");
732 assert_eq!(None, parser.try_read_note_alter(true));
733 parser.new_reader("");
734 assert_eq!(None, parser.try_read_note_alter(true));
735 }
736
737 #[test]
738 fn root_note_parsing() {
739 let mut parser = ChordParser::new();
740
741 match parser.parse("C") {
742 ChordParseResult::Failure(_) => panic!("Expected success"),
743 ChordParseResult::Success(Chord { note, .. }) => {
744 assert_eq!(note.pitch, Pitch::C);
745 assert_eq!(note.accidental, Accidental::Natural);
746 }
747 };
748
749 match parser.parse("d##") {
750 ChordParseResult::Failure(_) => panic!("Expected success"),
751 ChordParseResult::Success(Chord { note, .. }) => {
752 assert_eq!(note.pitch, Pitch::D);
753 assert_eq!(note.accidental, Accidental::DoubleSharp);
754 }
755 };
756
757 match parser.parse("dm") {
758 ChordParseResult::Failure(_) => panic!("Expected success"),
759 ChordParseResult::Success(Chord { note, .. }) => {
760 assert_eq!(note.pitch, Pitch::D);
761 assert_eq!(note.accidental, Accidental::Natural);
762 }
763 };
764
765 match parser.parse("F#m") {
766 ChordParseResult::Failure(_) => panic!("Expected success"),
767 ChordParseResult::Success(Chord { note, .. }) => {
768 assert_eq!(note.pitch, Pitch::F);
769 assert_eq!(note.accidental, Accidental::Sharp);
770 }
771 };
772
773 match parser.parse("Bbbm") {
774 ChordParseResult::Failure(_) => panic!("Expected success"),
775 ChordParseResult::Success(Chord { note, .. }) => {
776 assert_eq!(note.pitch, Pitch::B);
777 assert_eq!(note.accidental, Accidental::DoubleFlat);
778 }
779 };
780
781 match parser.parse("kdsk") {
782 ChordParseResult::Failure(ChordParseErrorKind::InvalidRoot) => (),
783 ChordParseResult::Failure(f) => panic!("Wrong failure, got: {:?}", f),
784 ChordParseResult::Success(_) => panic!("Expected failure"),
785 };
786 }
787
788 #[test]
789 fn chord_type_parsing() {
790 let mut parser = ChordParser::new();
791
792 match parser.parse("C") {
793 ChordParseResult::Failure(_) => panic!("Expected success"),
794 ChordParseResult::Success(Chord { chord_type, .. }) => {
795 assert_eq!(chord_type, ChordTriadType::Major);
796 }
797 };
798
799 match parser.parse("C7") {
800 ChordParseResult::Failure(_) => panic!("Expected success"),
801 ChordParseResult::Success(Chord { chord_type, .. }) => {
802 assert_eq!(chord_type, ChordTriadType::Major);
803 }
804 };
805
806 match parser.parse("CM") {
807 ChordParseResult::Failure(_) => panic!("Expected success"),
808 ChordParseResult::Success(Chord { chord_type, .. }) => {
809 assert_eq!(chord_type, ChordTriadType::Major);
810 }
811 };
812
813 match parser.parse("C#Maj7") {
814 ChordParseResult::Failure(_) => panic!("Expected success"),
815 ChordParseResult::Success(Chord { chord_type, .. }) => {
816 assert_eq!(chord_type, ChordTriadType::Major);
817 }
818 };
819
820 match parser.parse("Cbb-7") {
821 ChordParseResult::Failure(_) => panic!("Expected success"),
822 ChordParseResult::Success(Chord { chord_type, .. }) => {
823 assert_eq!(chord_type, ChordTriadType::Minor);
824 }
825 };
826
827 match parser.parse("Ebm11") {
828 ChordParseResult::Failure(_) => panic!("Expected success"),
829 ChordParseResult::Success(Chord { chord_type, .. }) => {
830 assert_eq!(chord_type, ChordTriadType::Minor);
831 }
832 };
833
834 match parser.parse("Dbmi13") {
835 ChordParseResult::Failure(_) => panic!("Expected success"),
836 ChordParseResult::Success(Chord { chord_type, .. }) => {
837 assert_eq!(chord_type, ChordTriadType::Minor);
838 }
839 };
840
841 match parser.parse("G##min9") {
842 ChordParseResult::Failure(_) => panic!("Expected success"),
843 ChordParseResult::Success(Chord { chord_type, .. }) => {
844 assert_eq!(chord_type, ChordTriadType::Minor);
845 }
846 };
847
848 match parser.parse("C+9") {
849 ChordParseResult::Failure(_) => panic!("Expected success"),
850 ChordParseResult::Success(Chord { chord_type, .. }) => {
851 assert_eq!(chord_type, ChordTriadType::Augmented);
852 }
853 };
854
855 match parser.parse("Caug9") {
856 ChordParseResult::Failure(_) => panic!("Expected success"),
857 ChordParseResult::Success(Chord { chord_type, .. }) => {
858 assert_eq!(chord_type, ChordTriadType::Augmented);
859 }
860 };
861
862 match parser.parse("C°7") {
863 ChordParseResult::Failure(_) => panic!("Expected success"),
864 ChordParseResult::Success(Chord { chord_type, .. }) => {
865 assert_eq!(chord_type, ChordTriadType::Diminished);
866 }
867 };
868
869 match parser.parse("Co7") {
870 ChordParseResult::Failure(_) => panic!("Expected success"),
871 ChordParseResult::Success(Chord { chord_type, .. }) => {
872 assert_eq!(chord_type, ChordTriadType::Diminished);
873 }
874 };
875
876 match parser.parse("Cdim7") {
877 ChordParseResult::Failure(_) => panic!("Expected success"),
878 ChordParseResult::Success(Chord { chord_type, .. }) => {
879 assert_eq!(chord_type, ChordTriadType::Diminished);
880 }
881 };
882 }
883
884 #[test]
885 fn chord_alteration_parsing_add() {
886 let mut parser = ChordParser::new();
887
888 match parser.parse("C add 9") {
889 ChordParseResult::Failure(_) => panic!("Expected success"),
890 ChordParseResult::Success(Chord { alterations, .. }) => {
891 assert_eq!(alterations.alters().clone(), vec![ChordAlter::Add(ChordNoteAlter
892 {
893 interval: AlteredInterval::Ninth,
894 accidental: Accidental::Natural,
895 })]);
896 }
897 };
898
899 match parser.parse("C add +9 add b b4") {
900 ChordParseResult::Failure(_) => panic!("Expected success"),
901 ChordParseResult::Success(Chord { alterations, .. }) => {
902 assert_eq!(alterations.alters().clone(), vec![ChordAlter::Add(ChordNoteAlter
903 {
904 interval: AlteredInterval::Ninth,
905 accidental: Accidental::Sharp,
906 }),
907 ChordAlter::Add(
908 ChordNoteAlter {
909 interval: AlteredInterval::Fourth,
910 accidental: Accidental::DoubleFlat,
911 })]);
912 }
913 };
914
915 match parser.parse("C/+9/bb11/13") {
916 ChordParseResult::Failure(_) => panic!("Expected success"),
917 ChordParseResult::Success(Chord { alterations, .. }) => {
918 assert_eq!(alterations.alters().clone(), vec![ChordAlter::Add(ChordNoteAlter
919 {
920 interval: AlteredInterval::Ninth,
921 accidental: Accidental::Sharp,
922 }),
923 ChordAlter::Add(
924 ChordNoteAlter {
925 interval: AlteredInterval::Eleventh,
926 accidental: Accidental::DoubleFlat,
927 }),
928 ChordAlter::Add(
929 ChordNoteAlter {
930 interval: AlteredInterval::Thirteenth,
931 accidental: Accidental::Natural,
932 }
933 )]);
934 }
935 };
936
937 match parser.parse("Cadd") {
938 ChordParseResult::Failure(ChordParseErrorKind::InvalidAlterations) => (),
939 ChordParseResult::Failure(f) => panic!("Wrong failure, got: {:?}", f),
940 ChordParseResult::Success(_) => panic!("Expected failure"),
941 };
942 }
943
944 #[test]
945 fn chord_alteration_parsing_sus() {
946 let mut parser = ChordParser::new();
947
948 match parser.parse("Csus") {
949 ChordParseResult::Failure(_) => panic!("Expected success"),
950 ChordParseResult::Success(Chord { alterations, .. }) => {
951 assert_eq!(alterations.alters().clone(), vec![ChordAlter::Suspended(
952 AlteredInterval::Fourth,
953 )]);
954 }
955 };
956
957 match parser.parse("Csus4") {
958 ChordParseResult::Failure(_) => panic!("Expected success"),
959 ChordParseResult::Success(Chord { alterations, .. }) => {
960 assert_eq!(alterations.alters().clone(), vec![ChordAlter::Suspended(
961 AlteredInterval::Fourth,
962 )]);
963 }
964 };
965
966 match parser.parse("Csus2") {
967 ChordParseResult::Failure(_) => panic!("Expected success"),
968 ChordParseResult::Success(Chord { alterations, .. }) => {
969 assert_eq!(alterations.alters().clone(), vec![ChordAlter::Suspended(
970 AlteredInterval::Second,
971 )]);
972 }
973 };
974
975 match parser.parse("Csus13") {
976 ChordParseResult::Failure(_) => panic!("Expected success"),
977 ChordParseResult::Success(Chord { alterations, .. }) => {
978 assert_eq!(alterations.alters().clone(), vec![ChordAlter::Add(ChordNoteAlter
979 {
980 interval: AlteredInterval::Ninth,
981 accidental: Accidental::Natural,
982 }),
983 ChordAlter::Add(
984 ChordNoteAlter {
985 interval: AlteredInterval::Thirteenth,
986 accidental: Accidental::Natural,
987 }),
988 ChordAlter::Suspended(
989 AlteredInterval::Fourth
990 )]);
991 }
992 };
993
994 match parser.parse("Csus2/4") {
995 ChordParseResult::Failure(_) => panic!("Expected success"),
996 ChordParseResult::Success(Chord { alterations, .. }) => {
997 assert_eq!(alterations.alters().clone(), vec![ChordAlter::Suspended(
998 AlteredInterval::Second,
999 ),
1000 ChordAlter::Add(ChordNoteAlter {
1001 interval: AlteredInterval::Fourth,
1002 accidental: Accidental::Natural,
1003 })]);
1004 }
1005 };
1006
1007 match parser.parse("Csusb4") {
1008 ChordParseResult::Failure(ChordParseErrorKind::InvalidAlterations) => (),
1009 ChordParseResult::Failure(f) => panic!("Wrong failure, got: {:?}", f),
1010 ChordParseResult::Success(_) => panic!("Expected failure"),
1011 };
1012
1013 match parser.parse("Fsuswoahgibberish") {
1014 ChordParseResult::Failure(ChordParseErrorKind::InvalidAlterations) => (),
1015 ChordParseResult::Failure(f) => panic!("Wrong failure, got: {:?}", f),
1016 ChordParseResult::Success(_) => panic!("Expected failure"),
1017 };
1018 }
1019
1020 #[test]
1021 fn chord_alteration_parsing_maj() {
1022 let mut parser = ChordParser::new();
1023
1024 match parser.parse("Cmaj7") {
1025 ChordParseResult::Failure(_) => panic!("Expected success"),
1026 ChordParseResult::Success(Chord { alterations, .. }) => {
1027 assert_eq!(alterations.seventh, Seventh::Major);
1028 assert_eq!(alterations.alters().clone(), vec![]);
1029 }
1030 };
1031
1032 match parser.parse("CΔ9") {
1033 ChordParseResult::Failure(_) => panic!("Expected success"),
1034 ChordParseResult::Success(Chord { alterations, .. }) => {
1035 assert_eq!(alterations.seventh, Seventh::Major);
1036 assert_eq!(alterations.alters().clone(), vec![ChordAlter::Add(ChordNoteAlter
1037 {
1038 interval: AlteredInterval::Ninth,
1039 accidental: Accidental::Natural,
1040 })]);
1041 }
1042 };
1043
1044 match parser.parse("Cmaj11") {
1045 ChordParseResult::Failure(_) => panic!("Expected success"),
1046 ChordParseResult::Success(Chord { alterations, .. }) => {
1047 assert_eq!(alterations.seventh, Seventh::Major);
1048 assert_eq!(alterations.alters().clone(), vec![ChordAlter::Suspended(AlteredInterval::Fourth)]);
1049 }
1050 };
1051
1052 match parser.parse("Cmaj13") {
1053 ChordParseResult::Failure(_) => panic!("Expected success"),
1054 ChordParseResult::Success(Chord { alterations, .. }) => {
1055 assert_eq!(alterations.seventh, Seventh::Major);
1056 assert_eq!(alterations.alters().clone(), vec![ChordAlter::Add(ChordNoteAlter
1057 {
1058 interval: AlteredInterval::Ninth,
1059 accidental: Accidental::Natural,
1060 }),
1061 ChordAlter::Add(
1062 ChordNoteAlter {
1063 interval: AlteredInterval::Thirteenth,
1064 accidental: Accidental::Natural,
1065 })]);
1066 }
1067 };
1068
1069 match parser.parse("Cmaj") {
1070 ChordParseResult::Failure(_) => panic!("Expected success"),
1071 ChordParseResult::Success(Chord { alterations, .. }) => {
1072 assert_eq!(alterations.seventh, Seventh::None);
1073 assert_eq!(alterations.alters().clone(), vec![]);
1074 }
1075 };
1076
1077 match parser.parse("Cmaj10") {
1078 ChordParseResult::Failure(ChordParseErrorKind::InvalidAlterations) => (),
1079 ChordParseResult::Failure(f) => panic!("Wrong failure, got: {:?}", f),
1080 ChordParseResult::Success(_) => panic!("Expected failure"),
1081 };
1082 }
1083
1084 #[test]
1085 fn chord_alteration_parsing_dom() {
1086 let mut parser = ChordParser::new();
1087
1088 match parser.parse("C2") {
1089 ChordParseResult::Failure(_) => panic!("Expected success"),
1090 ChordParseResult::Success(Chord { alterations, .. }) => {
1091 assert_eq!(alterations.seventh, Seventh::Flat);
1092 assert_eq!(alterations.alters().clone(), vec![ChordAlter::Add(ChordNoteAlter
1093 {
1094 interval: AlteredInterval::Second,
1095 accidental: Accidental::Natural,
1096 })]);
1097 }
1098 };
1099
1100 match parser.parse("Cdom7") {
1101 ChordParseResult::Failure(_) => panic!("Expected success"),
1102 ChordParseResult::Success(Chord { alterations, .. }) => {
1103 assert_eq!(alterations.seventh, Seventh::Flat);
1104 assert_eq!(alterations.alters().clone(), vec![]);
1105 }
1106 };
1107
1108 match parser.parse("C9") {
1109 ChordParseResult::Failure(_) => panic!("Expected success"),
1110 ChordParseResult::Success(Chord { alterations, .. }) => {
1111 assert_eq!(alterations.seventh, Seventh::Flat);
1112 assert_eq!(alterations.alters().clone(), vec![ChordAlter::Add(ChordNoteAlter
1113 {
1114 interval: AlteredInterval::Ninth,
1115 accidental: Accidental::Natural,
1116 })]);
1117 }
1118 };
1119
1120 match parser.parse("C11") {
1121 ChordParseResult::Failure(_) => panic!("Expected success"),
1122 ChordParseResult::Success(Chord { alterations, .. }) => {
1123 assert_eq!(alterations.seventh, Seventh::Flat);
1124 assert_eq!(alterations.alters().clone(), vec![ChordAlter::Suspended(AlteredInterval::Fourth)]);
1125 }
1126 };
1127
1128 match parser.parse("C13") {
1129 ChordParseResult::Failure(_) => panic!("Expected success"),
1130 ChordParseResult::Success(Chord { alterations, .. }) => {
1131 assert_eq!(alterations.seventh, Seventh::Flat);
1132 assert_eq!(alterations.alters().clone(), vec![ChordAlter::Add(ChordNoteAlter
1133 {
1134 interval: AlteredInterval::Ninth,
1135 accidental: Accidental::Natural,
1136 }),
1137 ChordAlter::Add(
1138 ChordNoteAlter {
1139 interval: AlteredInterval::Thirteenth,
1140 accidental: Accidental::Natural,
1141 })]);
1142 }
1143 };
1144
1145 match parser.parse("C10") {
1146 ChordParseResult::Failure(ChordParseErrorKind::InvalidAlterations) => (),
1147 ChordParseResult::Failure(f) => panic!("Wrong failure, got: {:?}", f),
1148 ChordParseResult::Success(_) => panic!("Expected failure"),
1149 };
1150 }
1151
1152 #[test]
1153 fn chord_alteration_parsing_alters() {
1154 let mut parser = ChordParser::new();
1155
1156 match parser.parse("Cmaj7##5b13+9-10") {
1157 ChordParseResult::Failure(_) => panic!("Expected success"),
1158 ChordParseResult::Success(Chord { alterations, .. }) => {
1159 assert_eq!(alterations.seventh, Seventh::Major);
1160 assert_eq!(alterations.alters().clone(), vec![ChordAlter::Add(ChordNoteAlter
1161 {
1162 interval: AlteredInterval::Fifth,
1163 accidental: Accidental::DoubleSharp,
1164 }
1165 ),
1166 ChordAlter::Add(
1167 ChordNoteAlter {
1168 interval: AlteredInterval::Thirteenth,
1169 accidental: Accidental::Flat,
1170 }
1171 ),
1172 ChordAlter::Add(
1173 ChordNoteAlter {
1174 interval: AlteredInterval::Ninth,
1175 accidental: Accidental::Sharp,
1176 }
1177 ),
1178 ChordAlter::Add(
1179 ChordNoteAlter {
1180 interval: AlteredInterval::Tenth,
1181 accidental: Accidental::Flat,
1182 }
1183 )]);
1184 }
1185 };
1186
1187 match parser.parse("C7#25") {
1188 ChordParseResult::Failure(ChordParseErrorKind::InvalidAlterations) => (),
1189 ChordParseResult::Failure(f) => panic!("Wrong failure, got: {:?}", f),
1190 ChordParseResult::Success(_) => panic!("Expected failure"),
1191 };
1192 }
1193
1194 #[test]
1195 fn chord_alteration_parsing_6() {
1196 let mut parser = ChordParser::new();
1197
1198 match parser.parse("Cm6") {
1199 ChordParseResult::Failure(_) => panic!("Expected success"),
1200 ChordParseResult::Success(Chord { alterations, .. }) => {
1201 assert_eq!(alterations.seventh, Seventh::None);
1202 assert_eq!(alterations.alters().clone(), vec![ChordAlter::Add(ChordNoteAlter
1203 {
1204 interval: AlteredInterval::Sixth,
1205 accidental: Accidental::Natural,
1206 }
1207 )]);
1208 }
1209 };
1210
1211 match parser.parse("Fmaj6/9") {
1212 ChordParseResult::Failure(_) => panic!("Expected success"),
1213 ChordParseResult::Success(Chord { alterations, .. }) => {
1214 assert_eq!(alterations.seventh, Seventh::None);
1215 assert_eq!(alterations.alters().clone(), vec![ChordAlter::Add(ChordNoteAlter
1216 {
1217 interval: AlteredInterval::Sixth,
1218 accidental: Accidental::Natural,
1219 }
1220 ),
1221 ChordAlter::Add(
1222 ChordNoteAlter {
1223 interval: AlteredInterval::Ninth,
1224 accidental: Accidental::Natural,
1225 }
1226 )]);
1227 }
1228 };
1229 }
1230
1231 #[test]
1232 fn chord_alteration_parsing_alters_list() {
1233 let mut parser = ChordParser::new();
1234
1235 match parser.parse("Cm(maj7,9,b13,+11)") {
1236 ChordParseResult::Failure(_) => panic!("Expected success"),
1237 ChordParseResult::Success(Chord { chord_type, alterations, .. }) => {
1238 assert_eq!(chord_type, ChordTriadType::Minor);
1239 assert_eq!(alterations.seventh, Seventh::Major);
1240 assert_eq!(alterations.alters().clone(), vec![ChordAlter::Add(ChordNoteAlter
1241 {
1242 interval: AlteredInterval::Ninth,
1243 accidental: Accidental::Natural,
1244 }
1245 ),
1246 ChordAlter::Add(
1247 ChordNoteAlter {
1248 interval: AlteredInterval::Thirteenth,
1249 accidental: Accidental::Flat,
1250 }
1251 ),
1252 ChordAlter::Add(
1253 ChordNoteAlter {
1254 interval: AlteredInterval::Eleventh,
1255 accidental: Accidental::Sharp,
1256 }
1257 )]);
1258 }
1259 };
1260
1261 match parser.parse("C7(9,13,b9)") {
1262 ChordParseResult::Failure(ChordParseErrorKind::InvalidAlterations) => (),
1263 ChordParseResult::Failure(f) => panic!("Wrong failure, got: {:?}", f),
1264 ChordParseResult::Success(_) => panic!("Expected failure"),
1265 };
1266 }
1267
1268 #[test]
1269 fn chord_alteration_parsing_no() {
1270 let mut parser = ChordParser::new();
1271
1272 match parser.parse("C7(no3)") {
1273 ChordParseResult::Failure(_) => panic!("Expected success"),
1274 ChordParseResult::Success(Chord { alterations, .. }) => {
1275 assert_eq!(alterations.seventh, Seventh::Flat);
1276 assert_eq!(alterations.no, No::Third);
1277 }
1278 }
1279
1280 match parser.parse("Fmaj11no5") {
1281 ChordParseResult::Failure(_) => panic!("Expected success"),
1282 ChordParseResult::Success(Chord { alterations, .. }) => {
1283 assert_eq!(alterations.seventh, Seventh::Major);
1284 assert_eq!(alterations.no, No::Fifth);
1285 }
1286 }
1287 }
1288
1289 #[test]
1290 fn chord_alteration_parsing_slash() {
1291 let mut parser = ChordParser::new();
1292
1293 match parser.parse("C7/9/E") {
1294 ChordParseResult::Failure(_) => panic!("Expected success"),
1295 ChordParseResult::Success(Chord { alterations, .. }) => {
1296 assert_eq!(alterations.seventh, Seventh::Flat);
1297 assert_eq!(alterations.slash, Some(Note { pitch: Pitch::E, accidental: Accidental::Natural }));
1298 }
1299 }
1300
1301 match parser.parse("Fmaj11no5") {
1302 ChordParseResult::Failure(_) => panic!("Expected success"),
1303 ChordParseResult::Success(Chord { alterations, .. }) => {
1304 assert_eq!(alterations.seventh, Seventh::Major);
1305 assert_eq!(alterations.no, No::Fifth);
1306 }
1307 }
1308 }
1309
1310 #[test]
1311 fn chord_alteration_parsing_mix() {
1312 let mut parser = ChordParser::new();
1313
1314 match parser.parse("C+(maj7,9,b13,+11)add+2sus4") {
1315 ChordParseResult::Failure(_) => panic!("Expected success"),
1316 ChordParseResult::Success(Chord { chord_type, alterations, .. }) => {
1317 assert_eq!(chord_type, ChordTriadType::Augmented);
1318 assert_eq!(alterations.seventh, Seventh::Major);
1319 assert_eq!(alterations.alters().clone(), vec![ChordAlter::Add(ChordNoteAlter
1320 {
1321 interval: AlteredInterval::Ninth,
1322 accidental: Accidental::Natural,
1323 }
1324 ),
1325 ChordAlter::Add(
1326 ChordNoteAlter {
1327 interval: AlteredInterval::Thirteenth,
1328 accidental: Accidental::Flat,
1329 }
1330 ),
1331 ChordAlter::Add(
1332 ChordNoteAlter {
1333 interval: AlteredInterval::Eleventh,
1334 accidental: Accidental::Sharp,
1335 }
1336 ),
1337 ChordAlter::Add(
1338 ChordNoteAlter {
1339 interval: AlteredInterval::Second,
1340 accidental: Accidental::Sharp,
1341 }
1342 ),
1343 ChordAlter::Suspended(
1344 AlteredInterval::Fourth
1345 )]);
1346 }
1347 };
1348 }
1349}