chord_parser/
lib.rs

1//! This cargo provides a parser for musical chord signatures. Successful outputs return a parsed chord
2//! using a built-in representation.
3//! 
4//! # Simple Example
5//! ```
6//! use chord_parser::*;
7//! 
8//! let mut parser = ChordParser::new();
9//! 
10//! let result = parser.parse("Cmaj9");
11//! 
12//! match result {
13//!     ChordParseResult::Success(chord) => println!("{:?}", chord.alterations.seventh),
14//!     ChordParseResult::Failure(kind) => panic!("Expected successful parse!"),
15//! };
16//! 
17//! let result = parser.parse("E7(b9,b13)");
18//! 
19//! // Do something else...
20//! ```
21//! 
22//! # Advanced
23//! 
24//! For everything you can do with parsing, visit [`ChordParser`].
25//! 
26//! To examine the abstract representation of chord elements, visit [`chord`].
27
28/// Abstract representations for elements of a chord signature.
29pub mod chord;
30/// Utilities for parsing required by the crate.
31pub mod utils;
32
33use crate::chord::*;
34use crate::utils::*;
35
36/// The type of error returned by [`ChordParser`] parsing failure
37#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
38pub enum ChordParseErrorKind {
39    /// Couldn't parse the root note
40    InvalidRoot,
41    /// Invalid alterations syntax
42    InvalidAlterations,
43    /// Currently is not returned; reserved.
44    Unknown, // reserved
45}
46
47/// Result of the [`ChordParser`]'s parse.
48pub enum ChordParseResult {
49    /// Successfully parsed the chord signature
50    Success(Chord),
51    /// Invalid input
52    Failure(ChordParseErrorKind),
53}
54
55/// The chord parser used for parsing chord signatures into abstract representations defined in [`chord`] module
56pub struct ChordParser {
57    reader: TextReader,
58}
59
60impl ChordParser {
61    /// Creates a new instance of `ChordParser`.
62    /// Use `ChordParser::parse(&str)` to execute parsing on an input.
63    pub fn new() -> Self {
64        ChordParser { reader: TextReader::new() }
65    }
66
67    /// Parse a chord signature directly from the input.
68    /// Shorter than having to create an instance via `new` function.
69    /// 
70    /// # Examples
71    /// ```
72    /// use chord_parser::*;
73    /// 
74    /// let result = ChordParser::from_str("C#7b9"); // Parse directly from the input
75    /// 
76    /// match result {
77    ///     ChordParseResult::Success(chord) => (),
78    ///     ChordParseResult::Failure(error_kind) => (),
79    /// }
80    /// ```
81    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 { // we don't need numbers more than 2 digits for intervals
110            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()) { // double accidental
158                    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(&note) {
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();                                                    // make sure we start with the longest one
243                                                                            // to account for potential substring options
244
245        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        // ambiguous notation conversion rules:
264        // 5 = no3
265        //
266        // (dom/maj)2 = (dom/maj)7 + 2
267        // (dom/maj)7 = (dom/maj)7 (no conversion; not ambiguous)
268        // (dom/maj)9 = (dom/maj)7 + 9
269        // (dom/maj)11 = (dom/maj)7 + sus4 (might be a bit controversial but this is most common)
270        // (dom/maj)13 = (dom/maj)9 + 13
271        //
272        // sus = sus4
273        // sus2/4 = sus2 + add9
274        // sus13 = dom13 + sus4
275
276        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, // no3 chords
295                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) { // duplicate
331                        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() { // duplicate
338                        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 { // duplicate
376                    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 == '(' { // alteration enumeration
404                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) { // must be maj7
407                        if self.reader.is_end() || self.reader.next().unwrap() != '7' {
408                            return None;
409                        }
410                        if alters.seventh != Seventh::None {
411                            return None; // duplicate
412                        }
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 { // duplicate
426                            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) { // duplicate
440                                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) { // duplicate
466                        return None;
467                    }
468    
469                    alters.set_note(&alter);
470                    continue;
471                }
472
473                // slash chord
474                let note = match self.try_read_note() {
475                    Some(note) => note,
476                    None => return None,
477                };
478
479                if alters.slash != None { // duplicate
480                    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        // acceptable parsable chord types
495        // *nothing, ("M", "ma", "maj") (- followed by nothing) -> Major
496        // "m", "mi", "min", "-" -> Minor
497        // "+", "aug" -> Augmented
498        // "dim", "°", "o" -> Diminished
499
500        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"; // all equal in length
511
512        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    /// Parse a chord signature from a string slice.
579    /// 
580    /// # Examples
581    /// ```
582    /// use chord_parser::*;
583    /// 
584    /// let mut parser = ChordParser::new();
585    /// 
586    /// let result = parser.parse("C#7b9"); // Parse from the input
587    /// 
588    /// match result {
589    ///     ChordParseResult::Success(chord) => (),
590    ///     ChordParseResult::Failure(error_kind) => (),
591    /// }
592    /// ```
593    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    /// Tries to parse the interval from a string slice.
616    /// 
617    /// # Examples
618    /// ```
619    /// use chord_parser::*;
620    /// 
621    /// let mut parser = ChordParser::new();
622    /// 
623    /// let result = parser.try_parse_interval("13");
624    /// 
625    /// match result {
626    ///     Some(interval) => println!("Interval is: {:?}", interval),
627    ///     None => panic!("Expected success!"),
628    /// }
629    /// ```
630    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    /// Tries to parse the note alteration from a string slice.
637    /// 
638    /// # Examples
639    /// ```
640    /// use chord_parser::*;
641    /// 
642    /// let mut parser = ChordParser::new();
643    /// 
644    /// let result = parser.try_parse_note_alter("b13", true);
645    /// 
646    /// match result {
647    ///     Some(alter) => println!("Alteration is: {:?}", alter),
648    ///     None => panic!("Forgot an accidental?"),
649    /// }
650    /// ``` 
651    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"); // too long for interval needs
676        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}