Skip to main content

music21_rs/
error.rs

1use std::convert::Infallible;
2
3use thiserror::Error as ThisError;
4
5/// Result type returned by fallible `music21-rs` operations.
6pub type Result<T, E = Error> = std::result::Result<T, E>;
7
8/// Error variants produced by the crate's theory helpers.
9#[derive(Clone, Debug, Eq, PartialEq, ThisError)]
10#[non_exhaustive]
11pub enum Error {
12    /// Error associated with a generic music21-style object.
13    #[error("Music21Object error: {0}")]
14    Music21Object(String),
15    /// Error associated with chord construction or analysis.
16    #[error("Chord error: {0}")]
17    Chord(String),
18    /// Error associated with pitch construction, spelling or conversion.
19    #[error("Pitch error: {0}")]
20    Pitch(String),
21    /// Error associated with microtone construction or conversion.
22    #[error("Microtone error: {0}")]
23    Microtone(String),
24    /// Error associated with accidental parsing or conversion.
25    #[error("Accidental error: {0}")]
26    Accidental(String),
27    /// Error associated with generated chord-table lookup data.
28    #[error("ChordTables error: {0}")]
29    ChordTables(String),
30    /// Error associated with interval construction or conversion.
31    #[error("Interval error: {0}")]
32    Interval(String),
33    /// Error associated with step-name parsing or conversion.
34    #[error("StepName error: {0}")]
35    StepName(String),
36    /// Error associated with numeric pitch-class parsing.
37    #[error("PitchClass error: {0}")]
38    PitchClass(String),
39    /// Error associated with ordinal-name parsing.
40    #[error("Ordinal error: {0}")]
41    Ordinal(String),
42    /// Error associated with polyrhythm construction or timing.
43    #[error("Polyrhythm error: {0}")]
44    Polyrhythm(String),
45    /// Error associated with tuning-system parsing or lookup.
46    #[error("TuningSystem error: {0}")]
47    TuningSystem(String),
48    /// Error associated with MIDI import or export.
49    #[error("Midi error: {0}")]
50    Midi(String),
51    /// Error associated with analysis helpers.
52    #[error("Analysis error: {0}")]
53    Analysis(String),
54    /// Error associated with time-signature parsing or beat lookup.
55    #[error("Meter error: {0}")]
56    Meter(String),
57    /// Error associated with Xenakis sieve parsing or evaluation.
58    #[error("Sieve error: {0}")]
59    Sieve(String),
60    /// Error associated with duration values.
61    #[error("Duration error: {0}")]
62    Duration(String),
63    /// Error associated with keys and key signatures.
64    #[error("Key error: {0}")]
65    Key(String),
66    /// Error associated with scale degrees and realization.
67    #[error("Scale error: {0}")]
68    Scale(String),
69    /// Error associated with tempo marks.
70    #[error("Tempo error: {0}")]
71    Tempo(String),
72    /// Error associated with tone rows and serial transformations.
73    #[error("Serial error: {0}")]
74    Serial(String),
75    /// Error associated with ties, noteheads, stems and lyrics.
76    #[error("Notation error: {0}")]
77    Notation(String),
78    /// Error associated with note volumes.
79    #[error("Volume error: {0}")]
80    Volume(String),
81    /// Error associated with Harte chord labels and degrees.
82    #[error("Harte error: {0}")]
83    Harte(String),
84    /// A value the caller gave that nothing musical could be read from.
85    ///
86    /// music21 keeps this apart from its own exceptions — an octave written
87    /// before a pitch name, or a name that is not a string at all, is a
88    /// `ValueError` there and not a `PitchException` — and callers catch the
89    /// two separately, so the crate keeps them apart too.
90    #[error("Value error: {0}")]
91    Value(String),
92}
93
94impl From<Infallible> for Error {
95    fn from(value: Infallible) -> Self {
96        match value {}
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103    use std::error::Error as StdError;
104
105    #[test]
106    fn test_display_music21object() {
107        let err = Error::Music21Object("error message".to_string());
108        assert_eq!(format!("{err}"), "Music21Object error: error message");
109    }
110
111    #[test]
112    fn test_display_chord() {
113        let err = Error::Chord("chord error".to_string());
114        assert_eq!(format!("{err}"), "Chord error: chord error");
115    }
116
117    #[test]
118    fn test_display_pitch() {
119        let err = Error::Pitch("pitch error".to_string());
120        assert_eq!(format!("{err}"), "Pitch error: pitch error");
121    }
122
123    #[test]
124    fn test_display_microtone() {
125        let err = Error::Microtone("microtone error".to_string());
126        assert_eq!(format!("{err}"), "Microtone error: microtone error");
127    }
128
129    #[test]
130    fn test_display_accidental() {
131        let err = Error::Accidental("accidental error".to_string());
132        assert_eq!(format!("{err}"), "Accidental error: accidental error");
133    }
134
135    #[test]
136    fn test_display_chordtables() {
137        let err = Error::ChordTables("chordtables error".to_string());
138        assert_eq!(format!("{err}"), "ChordTables error: chordtables error");
139    }
140
141    #[test]
142    fn test_display_interval() {
143        let err = Error::Interval("interval error".to_string());
144        assert_eq!(format!("{err}"), "Interval error: interval error");
145    }
146
147    #[test]
148    fn test_display_stepname() {
149        let err = Error::StepName("step name error".to_string());
150        assert_eq!(format!("{err}"), "StepName error: step name error");
151    }
152
153    #[test]
154    fn test_display_ordinal() {
155        let err = Error::Ordinal("ordinal error".to_string());
156        assert_eq!(format!("{err}"), "Ordinal error: ordinal error");
157    }
158
159    #[test]
160    fn test_display_polyrhythm() {
161        let err = Error::Polyrhythm("polyrhythm error".to_string());
162        assert_eq!(format!("{err}"), "Polyrhythm error: polyrhythm error");
163    }
164
165    #[test]
166    fn test_display_tuningsystem() {
167        let err = Error::TuningSystem("tuning system error".to_string());
168        assert_eq!(format!("{err}"), "TuningSystem error: tuning system error");
169    }
170
171    #[test]
172    fn test_source_none() {
173        let errors = [
174            Error::Music21Object("music21".to_string()),
175            Error::Chord("chord".to_string()),
176            Error::Pitch("pitch".to_string()),
177            Error::Microtone("microtone".to_string()),
178            Error::Accidental("accidental".to_string()),
179            Error::ChordTables("chordtables".to_string()),
180            Error::Interval("interval".to_string()),
181            Error::StepName("step".to_string()),
182            Error::PitchClass("pitch class".to_string()),
183            Error::Ordinal("ordinal".to_string()),
184            Error::Polyrhythm("polyrhythm".to_string()),
185            Error::TuningSystem("tuning system".to_string()),
186            Error::Midi("midi".to_string()),
187            Error::Analysis("analysis".to_string()),
188        ];
189
190        for err in errors.iter() {
191            // Ensure that source() returns None for each Error.
192            assert!(
193                err.source().is_none(),
194                "Expected None for source() in {err:?}"
195            );
196        }
197    }
198
199    #[test]
200    fn test_all_errors_display() {
201        let cases = [
202            (
203                Error::Music21Object("music21".to_string()),
204                "Music21Object error: music21",
205            ),
206            (Error::Chord("chord".to_string()), "Chord error: chord"),
207            (Error::Pitch("pitch".to_string()), "Pitch error: pitch"),
208            (
209                Error::Microtone("microtone".to_string()),
210                "Microtone error: microtone",
211            ),
212            (
213                Error::Accidental("accidental".to_string()),
214                "Accidental error: accidental",
215            ),
216            (
217                Error::ChordTables("chordtables".to_string()),
218                "ChordTables error: chordtables",
219            ),
220            (
221                Error::Interval("interval".to_string()),
222                "Interval error: interval",
223            ),
224            (Error::StepName("step".to_string()), "StepName error: step"),
225            (
226                Error::PitchClass("pitchclass".to_string()),
227                "PitchClass error: pitchclass",
228            ),
229            (
230                Error::Ordinal("ordinal".to_string()),
231                "Ordinal error: ordinal",
232            ),
233            (
234                Error::Polyrhythm("polyrhythm".to_string()),
235                "Polyrhythm error: polyrhythm",
236            ),
237            (
238                Error::TuningSystem("tuning system".to_string()),
239                "TuningSystem error: tuning system",
240            ),
241            (Error::Midi("midi".to_string()), "Midi error: midi"),
242            (
243                Error::Analysis("analysis".to_string()),
244                "Analysis error: analysis",
245            ),
246            (Error::Serial("serial".to_string()), "Serial error: serial"),
247        ];
248
249        for (err, expected) in cases.iter() {
250            assert_eq!(format!("{err}"), *expected);
251            assert!(err.source().is_none());
252        }
253    }
254}