use std::fmt;
#[derive(Debug, Clone)]
pub enum TunesError {
SampleNotFound(String),
SectionNotFound(String),
TrackNotFound(String),
TemplateNotFound(String),
MarkerNotFound(String),
AudioEngineError(String),
InvalidEventType {
expected: String,
found: String,
operation: String,
},
WavReadError(String),
WavWriteError(String),
MidiError(String),
InvalidAudioFormat(String),
EffectError(String),
TimingError(String),
SequenceError(String),
IoError(String),
Other(String),
}
impl fmt::Display for TunesError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TunesError::SampleNotFound(name) => {
write!(
f,
"Sample '{}' not found. Load it first with comp.load_sample()",
name
)
}
TunesError::SectionNotFound(name) => {
write!(f, "Section '{}' not found", name)
}
TunesError::TrackNotFound(name) => {
write!(f, "Track '{}' not found", name)
}
TunesError::TemplateNotFound(name) => {
write!(
f,
"Template '{}' not found. Create it first with comp.track_template()",
name
)
}
TunesError::MarkerNotFound(name) => {
write!(f, "Marker '{}' not found", name)
}
TunesError::AudioEngineError(msg) => {
write!(f, "Audio engine error: {}", msg)
}
TunesError::InvalidEventType {
expected,
found,
operation,
} => {
write!(
f,
"Invalid event type for {}: expected {}, found {}",
operation, expected, found
)
}
TunesError::WavReadError(msg) => {
write!(f, "WAV read error: {}", msg)
}
TunesError::WavWriteError(msg) => {
write!(f, "WAV write error: {}", msg)
}
TunesError::MidiError(msg) => {
write!(f, "MIDI error: {}", msg)
}
TunesError::InvalidAudioFormat(msg) => {
write!(f, "Invalid audio format: {}", msg)
}
TunesError::EffectError(msg) => {
write!(f, "Effect error: {}", msg)
}
TunesError::TimingError(msg) => {
write!(f, "Timing error: {}", msg)
}
TunesError::SequenceError(msg) => {
write!(f, "Sequence error: {}", msg)
}
TunesError::IoError(msg) => {
write!(f, "IO error: {}", msg)
}
TunesError::Other(msg) => {
write!(f, "{}", msg)
}
}
}
}
impl std::error::Error for TunesError {}
impl From<std::io::Error> for TunesError {
fn from(err: std::io::Error) -> Self {
TunesError::IoError(err.to_string())
}
}
impl From<cpal::BuildStreamError> for TunesError {
fn from(err: cpal::BuildStreamError) -> Self {
TunesError::AudioEngineError(format!("Failed to build audio stream: {}", err))
}
}
impl From<cpal::PlayStreamError> for TunesError {
fn from(err: cpal::PlayStreamError) -> Self {
TunesError::AudioEngineError(format!("Failed to play audio stream: {}", err))
}
}
impl From<String> for TunesError {
fn from(err: String) -> Self {
TunesError::Other(err)
}
}
impl From<&str> for TunesError {
fn from(err: &str) -> Self {
TunesError::Other(err.to_string())
}
}
pub type Result<T> = std::result::Result<T, TunesError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err = TunesError::SampleNotFound("kick.wav".to_string());
assert_eq!(
err.to_string(),
"Sample 'kick.wav' not found. Load it first with comp.load_sample()"
);
}
#[test]
fn test_invalid_event_type_error() {
let err = TunesError::InvalidEventType {
expected: "NoteEvent".to_string(),
found: "DrumEvent".to_string(),
operation: "vibrato".to_string(),
};
assert_eq!(
err.to_string(),
"Invalid event type for vibrato: expected NoteEvent, found DrumEvent"
);
}
#[test]
fn test_from_string() {
let err: TunesError = "Something went wrong".into();
assert_eq!(err.to_string(), "Something went wrong");
}
#[test]
fn test_io_error_conversion() {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let tunes_err: TunesError = io_err.into();
assert!(matches!(tunes_err, TunesError::IoError(_)));
}
}