1use std::convert::Infallible;
2
3use thiserror::Error as ThisError;
4
5pub type Result<T, E = Error> = std::result::Result<T, E>;
7
8#[derive(Clone, Debug, Eq, PartialEq, ThisError)]
10#[non_exhaustive]
11pub enum Error {
12 #[error("Music21Object error: {0}")]
14 Music21Object(String),
15 #[error("Chord error: {0}")]
17 Chord(String),
18 #[error("Pitch error: {0}")]
20 Pitch(String),
21 #[error("Microtone error: {0}")]
23 Microtone(String),
24 #[error("Accidental error: {0}")]
26 Accidental(String),
27 #[error("ChordTables error: {0}")]
29 ChordTables(String),
30 #[error("Interval error: {0}")]
32 Interval(String),
33 #[error("StepName error: {0}")]
35 StepName(String),
36 #[error("PitchClass error: {0}")]
38 PitchClass(String),
39 #[error("Ordinal error: {0}")]
41 Ordinal(String),
42 #[error("Polyrhythm error: {0}")]
44 Polyrhythm(String),
45 #[error("TuningSystem error: {0}")]
47 TuningSystem(String),
48 #[error("Midi error: {0}")]
51 Midi(Box<FormatError>),
52 #[error("Analysis error: {0}")]
54 Analysis(String),
55 #[error("Meter error: {0}")]
57 Meter(String),
58 #[error("Sieve error: {0}")]
60 Sieve(String),
61 #[error("Metadata error: {0}")]
64 Metadata(String),
65 #[error("Duration error: {0}")]
67 Duration(String),
68 #[error("Key error: {0}")]
70 Key(String),
71 #[error("Scale error: {0}")]
73 Scale(String),
74 #[error("Tempo error: {0}")]
76 Tempo(String),
77 #[error("Instrument error: {0}")]
79 Instrument(String),
80 #[error("Figured bass error: {0}")]
82 FiguredBass(String),
83 #[error("ABC error: {0}")]
85 Abc(Box<FormatError>),
86 #[error("Humdrum error: {0}")]
88 Humdrum(Box<FormatError>),
89 #[error("XML error: {0}")]
91 Xml(Box<FormatError>),
92 #[error("MEI error: {0}")]
94 Mei(Box<FormatError>),
95 #[error("RomanText error: {0}")]
97 RomanText(Box<FormatError>),
98 #[error("Capella error: {0}")]
100 Capella(Box<FormatError>),
101 #[error("MuseData error: {0}")]
103 MuseData(Box<FormatError>),
104 #[error("NoteWorthy error: {0}")]
106 Noteworthy(Box<FormatError>),
107 #[error("MuseScore error: {0}")]
110 MuseScore(Box<FormatError>),
111 #[error("Expression error: {0}")]
113 Expression(String),
114 #[error("Clef error: {0}")]
116 Clef(String),
117 #[error("Serial error: {0}")]
119 Serial(String),
120 #[error("Notation error: {0}")]
122 Notation(String),
123 #[error("Volume error: {0}")]
125 Volume(String),
126 #[error("Stream error: {0}")]
129 Stream(String),
130
131 #[error("Feature error: {0}")]
134 Feature(String),
135 #[error("Text error: {0}")]
138 Text(String),
139 #[error("Search error: {0}")]
142 Search(String),
143 #[error("Tablature error: {0}")]
146 Tablature(String),
147 #[error("Tree error: {0}")]
150 Tree(String),
151 #[error("OMR error: {0}")]
155 Omr(String),
156 #[error("Variant error: {0}")]
160 Variant(String),
161 #[cfg(feature = "musicxml")]
164 #[error("MusicXML export error: {0}")]
165 MusicXml(Box<FormatError>),
166 #[error("Value error: {0}")]
173 Value(String),
174 #[error("{0}")]
178 Placed(Box<PlacedError>),
179 #[error("Environment error: {0}")]
182 Environment(String),
183}
184
185#[derive(Clone, Debug, Eq, PartialEq)]
198#[non_exhaustive]
199pub struct PlacedError {
200 pub error: Error,
202 pub position: Position,
204}
205
206impl std::fmt::Display for PlacedError {
207 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208 self.error.fmt(f)
209 }
210}
211
212#[derive(Clone, Debug, Eq, PartialEq)]
228#[non_exhaustive]
229pub struct FormatError {
230 pub kind: FormatErrorKind,
232 pub position: Option<Position>,
234 pub message: String,
236}
237
238impl FormatError {
239 pub fn new(kind: FormatErrorKind, message: impl Into<String>) -> Self {
241 Self {
242 kind,
243 position: None,
244 message: message.into(),
245 }
246 }
247
248 #[must_use]
250 pub fn with_position(mut self, position: Position) -> Self {
251 self.position = Some(position);
252 self
253 }
254}
255
256impl std::fmt::Display for FormatError {
257 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
258 f.write_str(&self.message)
259 }
260}
261
262#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
264#[non_exhaustive]
265pub enum FormatErrorKind {
266 Malformed,
270 Invalid,
273 Unsupported,
276 TooLarge,
281 NotFound,
284 External,
286}
287
288#[derive(Clone, Debug, Default, Eq, PartialEq, Hash)]
292#[non_exhaustive]
293pub struct Position {
294 pub line: Option<usize>,
296 pub column: Option<usize>,
298 pub measure: Option<String>,
301 pub part: Option<String>,
303 pub track: Option<usize>,
305 pub tick: Option<u64>,
307}
308
309impl Position {
310 pub fn line(line: usize) -> Self {
312 Self {
313 line: Some(line),
314 ..Self::default()
315 }
316 }
317
318 pub fn measure(number: impl Into<String>) -> Self {
320 Self {
321 measure: Some(number.into()),
322 ..Self::default()
323 }
324 }
325
326 pub fn track(index: usize) -> Self {
328 Self {
329 track: Some(index),
330 ..Self::default()
331 }
332 }
333
334 pub(crate) fn in_text(text: &str, at: usize) -> Self {
337 let before = &text[..at];
338 let line = before.matches('\n').count() + 1;
339 let line_start = before.rfind('\n').map_or(0, |newline| newline + 1);
340 let column = before[line_start..].chars().count() + 1;
341 Self::line(line).with_column(column)
342 }
343
344 #[must_use]
346 pub fn with_column(mut self, column: usize) -> Self {
347 self.column = Some(column);
348 self
349 }
350
351 #[must_use]
353 pub fn in_part(mut self, part: impl Into<String>) -> Self {
354 self.part = Some(part.into());
355 self
356 }
357
358 #[must_use]
360 pub fn at_tick(mut self, tick: u64) -> Self {
361 self.tick = Some(tick);
362 self
363 }
364
365 fn fill_from(&mut self, outer: Position) {
368 self.line = self.line.or(outer.line);
369 self.column = self.column.or(outer.column);
370 self.measure = self.measure.take().or(outer.measure);
371 self.part = self.part.take().or(outer.part);
372 self.track = self.track.or(outer.track);
373 self.tick = self.tick.or(outer.tick);
374 }
375}
376
377impl std::fmt::Display for Position {
378 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
380 let mut said = Vec::new();
381 if let Some(part) = &self.part {
382 said.push(format!("part {part}"));
383 }
384 if let Some(measure) = &self.measure {
385 said.push(format!("measure {measure}"));
386 }
387 if let Some(track) = self.track {
388 said.push(format!("track {track}"));
389 }
390 if let Some(tick) = self.tick {
391 said.push(format!("tick {tick}"));
392 }
393 if let Some(line) = self.line {
394 said.push(format!("line {line}"));
395 }
396 if let Some(column) = self.column {
397 said.push(format!("column {column}"));
398 }
399 f.write_str(&said.join(", "))
400 }
401}
402
403macro_rules! format_constructors {
406 ($($(#[$attribute:meta])* $name:ident => $variant:ident,)*) => {
407 impl Error {
408 $(
409 $(#[$attribute])*
410 pub(crate) fn $name(message: impl Into<String>) -> Self {
411 Self::$variant(Box::new(FormatError::new(FormatErrorKind::Invalid, message)))
412 }
413 )*
414 }
415 };
416}
417
418format_constructors! {
419 midi => Midi,
420 abc => Abc,
421 humdrum => Humdrum,
422 xml => Xml,
423 mei => Mei,
424 roman_text => RomanText,
425 capella => Capella,
426 muse_data => MuseData,
427 noteworthy => Noteworthy,
428 muse_score => MuseScore,
429 #[cfg(feature = "musicxml")]
430 music_xml => MusicXml,
431}
432
433impl Error {
434 pub fn unplaced(&self) -> &Error {
437 match self {
438 Self::Placed(placed) => &placed.error,
439 _ => self,
440 }
441 }
442
443 pub fn format_error(&self) -> Option<&FormatError> {
446 match self.unplaced() {
447 Self::Midi(error)
448 | Self::Abc(error)
449 | Self::Humdrum(error)
450 | Self::Xml(error)
451 | Self::Mei(error)
452 | Self::RomanText(error)
453 | Self::Capella(error)
454 | Self::MuseData(error)
455 | Self::Noteworthy(error)
456 | Self::MuseScore(error) => Some(error.as_ref()),
457 #[cfg(feature = "musicxml")]
458 Self::MusicXml(error) => Some(error.as_ref()),
459 _ => None,
460 }
461 }
462
463 fn format_error_mut(&mut self) -> Option<&mut FormatError> {
464 match self {
465 Self::Midi(error)
466 | Self::Abc(error)
467 | Self::Humdrum(error)
468 | Self::Xml(error)
469 | Self::Mei(error)
470 | Self::RomanText(error)
471 | Self::Capella(error)
472 | Self::MuseData(error)
473 | Self::Noteworthy(error)
474 | Self::MuseScore(error) => Some(error.as_mut()),
475 #[cfg(feature = "musicxml")]
476 Self::MusicXml(error) => Some(error.as_mut()),
477 _ => None,
478 }
479 }
480
481 pub fn position(&self) -> Option<&Position> {
483 if let Self::Placed(placed) = self {
484 return Some(&placed.position);
485 }
486 self.format_error()?.position.as_ref()
487 }
488
489 fn position_mut(&mut self) -> Option<&mut Position> {
491 if let Self::Placed(placed) = self {
492 return Some(&mut placed.position);
493 }
494 self.format_error_mut()?.position.as_mut()
495 }
496
497 #[must_use]
499 pub(crate) fn of_kind(mut self, kind: FormatErrorKind) -> Self {
500 if let Some(error) = self.format_error_mut() {
501 error.kind = kind;
502 }
503 self
504 }
505
506 #[must_use]
510 pub(crate) fn lines_down(mut self, before: usize) -> Self {
511 let line = self
512 .position_mut()
513 .and_then(|position| position.line.as_mut());
514 if let Some(line) = line {
515 *line += before;
516 }
517 self
518 }
519
520 #[must_use]
525 pub(crate) fn placed(mut self, position: impl FnOnce() -> Position) -> Self {
526 if let Some(known) = self.position_mut() {
527 known.fill_from(position());
528 return self;
529 }
530 if let Some(error) = self.format_error_mut() {
531 error.position = Some(position());
532 return self;
533 }
534 Self::Placed(Box::new(PlacedError {
535 error: self,
536 position: position(),
537 }))
538 }
539}
540
541impl From<Infallible> for Error {
542 fn from(value: Infallible) -> Self {
543 match value {}
544 }
545}
546
547#[cfg(test)]
548mod tests {
549 use super::*;
550 use std::error::Error as StdError;
551
552 #[test]
553 fn test_display_music21object() {
554 let err = Error::Music21Object("error message".to_string());
555 assert_eq!(format!("{err}"), "Music21Object error: error message");
556 }
557
558 #[test]
559 fn test_display_chord() {
560 let err = Error::Chord("chord error".to_string());
561 assert_eq!(format!("{err}"), "Chord error: chord error");
562 }
563
564 #[test]
565 fn test_display_pitch() {
566 let err = Error::Pitch("pitch error".to_string());
567 assert_eq!(format!("{err}"), "Pitch error: pitch error");
568 }
569
570 #[test]
571 fn test_display_microtone() {
572 let err = Error::Microtone("microtone error".to_string());
573 assert_eq!(format!("{err}"), "Microtone error: microtone error");
574 }
575
576 #[test]
577 fn test_display_accidental() {
578 let err = Error::Accidental("accidental error".to_string());
579 assert_eq!(format!("{err}"), "Accidental error: accidental error");
580 }
581
582 #[test]
583 fn test_display_chordtables() {
584 let err = Error::ChordTables("chordtables error".to_string());
585 assert_eq!(format!("{err}"), "ChordTables error: chordtables error");
586 }
587
588 #[test]
589 fn test_display_interval() {
590 let err = Error::Interval("interval error".to_string());
591 assert_eq!(format!("{err}"), "Interval error: interval error");
592 }
593
594 #[test]
595 fn test_display_stepname() {
596 let err = Error::StepName("step name error".to_string());
597 assert_eq!(format!("{err}"), "StepName error: step name error");
598 }
599
600 #[test]
601 fn test_display_ordinal() {
602 let err = Error::Ordinal("ordinal error".to_string());
603 assert_eq!(format!("{err}"), "Ordinal error: ordinal error");
604 }
605
606 #[test]
607 fn test_display_polyrhythm() {
608 let err = Error::Polyrhythm("polyrhythm error".to_string());
609 assert_eq!(format!("{err}"), "Polyrhythm error: polyrhythm error");
610 }
611
612 #[test]
613 fn test_display_tuningsystem() {
614 let err = Error::TuningSystem("tuning system error".to_string());
615 assert_eq!(format!("{err}"), "TuningSystem error: tuning system error");
616 }
617
618 #[test]
619 fn a_format_error_shows_as_its_message_under_its_format() {
620 let error = Error::abc("cannot read the meter \"x\"").placed(|| Position::line(3));
621 assert_eq!(error.to_string(), "ABC error: cannot read the meter \"x\"");
622 assert_eq!(error.position(), Some(&Position::line(3)));
623 assert_eq!(error.format_error().unwrap().kind, FormatErrorKind::Invalid);
624 assert!(Error::Pitch("pitch".to_string()).format_error().is_none());
625 }
626
627 #[test]
628 fn a_position_said_inside_keeps_what_it_says() {
629 let error = Error::midi("a tempo of no time to the quarter")
630 .placed(|| Position::default().at_tick(96))
631 .placed(|| Position::track(2).at_tick(0))
632 .of_kind(FormatErrorKind::Malformed);
633 let position = error.position().unwrap();
634 assert_eq!(position, &Position::track(2).at_tick(96));
635 assert_eq!(position.to_string(), "track 2, tick 96");
636 assert_eq!(
637 Position::measure("12").in_part("P1").to_string(),
638 "part P1, measure 12"
639 );
640 assert_eq!(
641 Position::line(4).with_column(2).to_string(),
642 "line 4, column 2"
643 );
644 let shifted = Error::humdrum("x")
645 .placed(|| Position::line(2))
646 .lines_down(3);
647 assert_eq!(shifted.position(), Some(&Position::line(5)));
648 }
649
650 #[test]
651 fn an_error_of_no_format_is_placed_around_itself() {
652 let error = Error::Pitch("cannot spell it".to_string())
653 .placed(|| Position::line(4))
654 .placed(|| Position::measure("2").with_column(9))
655 .lines_down(1);
656 assert_eq!(error.to_string(), "Pitch error: cannot spell it");
657 assert_eq!(
658 error.unplaced(),
659 &Error::Pitch("cannot spell it".to_string())
660 );
661 let mut wanted = Position::line(5).with_column(9);
662 wanted.measure = Some("2".to_string());
663 assert_eq!(error.position(), Some(&wanted));
664 assert!(error.format_error().is_none());
665
666 let read = Error::abc("x").placed(|| Position::line(1));
667 assert_eq!(read.unplaced(), &read);
668 }
669
670 #[test]
671 fn test_source_none() {
672 let errors = [
673 Error::Music21Object("music21".to_string()),
674 Error::Chord("chord".to_string()),
675 Error::Pitch("pitch".to_string()),
676 Error::Microtone("microtone".to_string()),
677 Error::Accidental("accidental".to_string()),
678 Error::ChordTables("chordtables".to_string()),
679 Error::Interval("interval".to_string()),
680 Error::StepName("step".to_string()),
681 Error::PitchClass("pitch class".to_string()),
682 Error::Ordinal("ordinal".to_string()),
683 Error::Polyrhythm("polyrhythm".to_string()),
684 Error::TuningSystem("tuning system".to_string()),
685 Error::midi("midi"),
686 Error::Analysis("analysis".to_string()),
687 ];
688
689 for err in errors.iter() {
690 assert!(
692 err.source().is_none(),
693 "Expected None for source() in {err:?}"
694 );
695 }
696 }
697
698 #[test]
699 fn test_all_errors_display() {
700 let cases = [
701 (
702 Error::Music21Object("music21".to_string()),
703 "Music21Object error: music21",
704 ),
705 (Error::Chord("chord".to_string()), "Chord error: chord"),
706 (Error::Pitch("pitch".to_string()), "Pitch error: pitch"),
707 (
708 Error::Microtone("microtone".to_string()),
709 "Microtone error: microtone",
710 ),
711 (
712 Error::Accidental("accidental".to_string()),
713 "Accidental error: accidental",
714 ),
715 (
716 Error::ChordTables("chordtables".to_string()),
717 "ChordTables error: chordtables",
718 ),
719 (
720 Error::Interval("interval".to_string()),
721 "Interval error: interval",
722 ),
723 (Error::StepName("step".to_string()), "StepName error: step"),
724 (
725 Error::PitchClass("pitchclass".to_string()),
726 "PitchClass error: pitchclass",
727 ),
728 (
729 Error::Ordinal("ordinal".to_string()),
730 "Ordinal error: ordinal",
731 ),
732 (
733 Error::Polyrhythm("polyrhythm".to_string()),
734 "Polyrhythm error: polyrhythm",
735 ),
736 (
737 Error::TuningSystem("tuning system".to_string()),
738 "TuningSystem error: tuning system",
739 ),
740 (Error::midi("midi"), "Midi error: midi"),
741 (
742 Error::Analysis("analysis".to_string()),
743 "Analysis error: analysis",
744 ),
745 (Error::Serial("serial".to_string()), "Serial error: serial"),
746 ];
747
748 for (err, expected) in cases.iter() {
749 assert_eq!(format!("{err}"), *expected);
750 assert!(err.source().is_none());
751 }
752 }
753}