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}
41
42impl fmt::Display for Error {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        match self {
45            Error::Music21Object(msg) => write!(f, "Music21Object error: {msg}"),
46            Error::Chord(msg) => write!(f, "Chord error: {msg}"),
47            Error::Pitch(msg) => write!(f, "Pitch error: {msg}"),
48            Error::Microtone(msg) => write!(f, "Microtone error: {msg}"),
49            Error::Accidental(msg) => write!(f, "Accidental error: {msg}"),
50            Error::ChordTables(msg) => write!(f, "ChordTables error: {msg}"),
51            Error::Interval(msg) => write!(f, "Interval error: {msg}"),
52            Error::StepName(msg) => write!(f, "StepName error: {msg}"),
53            Error::PitchClass(msg) => write!(f, "PitchClass error: {msg}"),
54            Error::PitchClassString(msg) => write!(f, "PitchClassString error: {msg}"),
55            Error::Ordinal(msg) => write!(f, "Ordinal error: {msg}"),
56            Error::Polyrhythm(msg) => write!(f, "Polyrhythm error: {msg}"),
57            Error::TuningSystem(msg) => write!(f, "TuningSystem error: {msg}"),
58            Error::Midi(msg) => write!(f, "Midi error: {msg}"),
59            Error::Analysis(msg) => write!(f, "Analysis error: {msg}"),
60        }
61    }
62}
63
64impl error::Error for Error {
65    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
66        None
67    }
68}
69
70impl From<Infallible> for Error {
71    fn from(value: Infallible) -> Self {
72        match value {}
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use std::error::Error as StdError;
80
81    #[test]
82    fn test_display_music21object() {
83        let err = Error::Music21Object("error message".to_string());
84        assert_eq!(format!("{err}"), "Music21Object error: error message");
85    }
86
87    #[test]
88    fn test_display_chord() {
89        let err = Error::Chord("chord error".to_string());
90        assert_eq!(format!("{err}"), "Chord error: chord error");
91    }
92
93    #[test]
94    fn test_display_pitch() {
95        let err = Error::Pitch("pitch error".to_string());
96        assert_eq!(format!("{err}"), "Pitch error: pitch error");
97    }
98
99    #[test]
100    fn test_display_microtone() {
101        let err = Error::Microtone("microtone error".to_string());
102        assert_eq!(format!("{err}"), "Microtone error: microtone error");
103    }
104
105    #[test]
106    fn test_display_accidental() {
107        let err = Error::Accidental("accidental error".to_string());
108        assert_eq!(format!("{err}"), "Accidental error: accidental error");
109    }
110
111    #[test]
112    fn test_display_chordtables() {
113        let err = Error::ChordTables("chordtables error".to_string());
114        assert_eq!(format!("{err}"), "ChordTables error: chordtables error");
115    }
116
117    #[test]
118    fn test_display_interval() {
119        let err = Error::Interval("interval error".to_string());
120        assert_eq!(format!("{err}"), "Interval error: interval error");
121    }
122
123    #[test]
124    fn test_display_stepname() {
125        let err = Error::StepName("step name error".to_string());
126        assert_eq!(format!("{err}"), "StepName error: step name error");
127    }
128
129    #[test]
130    fn test_display_pitchclassstring() {
131        let err = Error::PitchClassString("pitch class error".to_string());
132        assert_eq!(
133            format!("{err}"),
134            "PitchClassString error: pitch class error"
135        );
136    }
137
138    #[test]
139    fn test_display_ordinal() {
140        let err = Error::Ordinal("ordinal error".to_string());
141        assert_eq!(format!("{err}"), "Ordinal error: ordinal error");
142    }
143
144    #[test]
145    fn test_display_polyrhythm() {
146        let err = Error::Polyrhythm("polyrhythm error".to_string());
147        assert_eq!(format!("{err}"), "Polyrhythm error: polyrhythm error");
148    }
149
150    #[test]
151    fn test_display_tuningsystem() {
152        let err = Error::TuningSystem("tuning system error".to_string());
153        assert_eq!(format!("{err}"), "TuningSystem error: tuning system error");
154    }
155
156    #[test]
157    fn test_source_none() {
158        let errors = [
159            Error::Music21Object("music21".to_string()),
160            Error::Chord("chord".to_string()),
161            Error::Pitch("pitch".to_string()),
162            Error::Microtone("microtone".to_string()),
163            Error::Accidental("accidental".to_string()),
164            Error::ChordTables("chordtables".to_string()),
165            Error::Interval("interval".to_string()),
166            Error::StepName("step".to_string()),
167            Error::PitchClass("pitch class".to_string()),
168            Error::PitchClassString("pitch class".to_string()),
169            Error::Ordinal("ordinal".to_string()),
170            Error::Polyrhythm("polyrhythm".to_string()),
171            Error::TuningSystem("tuning system".to_string()),
172            Error::Midi("midi".to_string()),
173            Error::Analysis("analysis".to_string()),
174        ];
175
176        for err in errors.iter() {
177            // Ensure that source() returns None for each Error.
178            assert!(
179                err.source().is_none(),
180                "Expected None for source() in {err:?}"
181            );
182        }
183    }
184
185    #[test]
186    fn test_all_errors_display() {
187        let cases = [
188            (
189                Error::Music21Object("music21".to_string()),
190                "Music21Object error: music21",
191            ),
192            (Error::Chord("chord".to_string()), "Chord error: chord"),
193            (Error::Pitch("pitch".to_string()), "Pitch error: pitch"),
194            (
195                Error::Microtone("microtone".to_string()),
196                "Microtone error: microtone",
197            ),
198            (
199                Error::Accidental("accidental".to_string()),
200                "Accidental error: accidental",
201            ),
202            (
203                Error::ChordTables("chordtables".to_string()),
204                "ChordTables error: chordtables",
205            ),
206            (
207                Error::Interval("interval".to_string()),
208                "Interval error: interval",
209            ),
210            (Error::StepName("step".to_string()), "StepName error: step"),
211            (
212                Error::PitchClass("pitchclass".to_string()),
213                "PitchClass error: pitchclass",
214            ),
215            (
216                Error::PitchClassString("pitchclass".to_string()),
217                "PitchClassString error: pitchclass",
218            ),
219            (
220                Error::Ordinal("ordinal".to_string()),
221                "Ordinal error: ordinal",
222            ),
223            (
224                Error::Polyrhythm("polyrhythm".to_string()),
225                "Polyrhythm error: polyrhythm",
226            ),
227            (
228                Error::TuningSystem("tuning system".to_string()),
229                "TuningSystem error: tuning system",
230            ),
231            (Error::Midi("midi".to_string()), "Midi error: midi"),
232            (
233                Error::Analysis("analysis".to_string()),
234                "Analysis error: analysis",
235            ),
236        ];
237
238        for (err, expected) in cases.iter() {
239            assert_eq!(format!("{err}"), *expected);
240            assert!(err.source().is_none());
241        }
242    }
243}