Skip to main content

music21_rs/
error.rs

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