use thiserror::Error;
#[derive(Error, Debug)]
pub enum ParakeetError {
#[error(
"Failed to load model from {path}: {reason}\nModel variant: {variant}\nSuggestion: Verify model path exists and files are not corrupted. Check model was exported correctly."
)]
ModelLoadError {
path: String,
reason: String,
variant: String,
},
#[error(
"Transcription failed: {0}\nStage: {1}\nDetails: {2}\nSuggestion: Check audio format matches expected 16kHz mono PCM, and model state is valid"
)]
TranscriptionError(String, String, String),
#[error(
"Invalid audio format: {0}\nExpected sample rate: {1} Hz\nExpected channels: {2}\nActual sample rate: {3} Hz\nActual channels: {4}\nSuggestion: Resample audio to 16kHz mono before processing"
)]
InvalidAudioFormat(String, u32, u16, u32, u16),
#[error(
"Chunk processing failed: {0}\nChunk size: {1} samples\nExpected: {2} samples\nModel: {3}\nSuggestion: Use recommended chunk size from model.chunk_size() method"
)]
ChunkProcessingError(String, usize, usize, String),
#[error("Streaming operation failed: {0}\nModel variant: {1}\nOperation: {2}\nSuggestion: {3}")]
StreamingError(String, String, String, String),
#[error(
"Language not supported: {0}\nModel variant: {1}\nSupported languages: {2}\nSuggestion: Use TDT model for multilingual support or specify a supported language"
)]
LanguageNotSupported(String, String, String),
#[error("Parakeet library error: {0}\nOperation: {1}\nContext: {2}")]
ParakeetLibraryError(String, String, String),
#[error("IO error: {0}\nFile path: {1}\nOperation: {2}")]
IoError(std::io::Error, String, String),
}
impl ParakeetError {
pub fn transcription_error_detailed(
msg: impl Into<String>,
stage: impl Into<String>,
details: impl Into<String>,
) -> Self {
Self::TranscriptionError(msg.into(), stage.into(), details.into())
}
pub fn invalid_audio_format(
msg: impl Into<String>,
expected_rate: u32,
expected_channels: u16,
actual_rate: u32,
actual_channels: u16,
) -> Self {
Self::InvalidAudioFormat(
msg.into(),
expected_rate,
expected_channels,
actual_rate,
actual_channels,
)
}
pub fn chunk_processing_error(
msg: impl Into<String>,
chunk_size: usize,
expected_size: usize,
model: impl Into<String>,
) -> Self {
Self::ChunkProcessingError(msg.into(), chunk_size, expected_size, model.into())
}
pub fn streaming_error(
msg: impl Into<String>,
variant: impl Into<String>,
operation: impl Into<String>,
suggestion: impl Into<String>,
) -> Self {
Self::StreamingError(
msg.into(),
variant.into(),
operation.into(),
suggestion.into(),
)
}
pub fn language_not_supported(
lang: impl Into<String>,
variant: impl Into<String>,
supported: impl Into<String>,
) -> Self {
Self::LanguageNotSupported(lang.into(), variant.into(), supported.into())
}
}
pub type Result<T> = std::result::Result<T, ParakeetError>;
impl From<ParakeetError> for crate::error::STTError {
fn from(err: ParakeetError) -> Self {
match err {
ParakeetError::ModelLoadError {
path,
reason,
variant,
} => crate::error::STTError::ModelNotFound(
format!("{} (variant: {})", reason, variant),
path,
),
ParakeetError::TranscriptionError(msg, stage, details) => {
crate::error::STTError::TranscriptionFailed(
format!("{} (stage: {}, details: {})", msg, stage, details),
0.0,
16000,
)
}
ParakeetError::InvalidAudioFormat(msg, exp_rate, exp_ch, act_rate, act_ch) => {
crate::error::STTError::InvalidAudioFormat(msg, exp_rate, act_rate, exp_ch, act_ch)
}
ParakeetError::ChunkProcessingError(msg, chunk_size, expected, model) => {
crate::error::STTError::Other(
format!(
"Chunk processing error: {} (chunk: {} samples, expected: {} samples, model: {})",
msg, chunk_size, expected, model
),
"Parakeet provider".to_string(),
)
}
ParakeetError::StreamingError(msg, variant, operation, suggestion) => {
crate::error::STTError::StreamingNotSupported(format!(
"{} (variant: {}, operation: {}, suggestion: {})",
msg, variant, operation, suggestion
))
}
ParakeetError::LanguageNotSupported(lang, variant, supported) => {
crate::error::STTError::Other(
format!(
"Language '{}' not supported by {} (supported: {})",
lang, variant, supported
),
"Parakeet provider".to_string(),
)
}
ParakeetError::ParakeetLibraryError(msg, operation, context) => {
crate::error::STTError::ProviderError(
format!("{} (operation: {}, context: {})", msg, operation, context),
"Parakeet".to_string(),
)
}
ParakeetError::IoError(e, path, operation) => {
crate::error::STTError::IoError(e, operation, path)
}
}
}
}
impl From<parakeet_rs::Error> for ParakeetError {
fn from(err: parakeet_rs::Error) -> Self {
ParakeetError::ParakeetLibraryError(
err.to_string(),
"parakeet-rs operation".to_string(),
"wrapped error from parakeet-rs library".to_string(),
)
}
}
impl From<std::io::Error> for ParakeetError {
fn from(err: std::io::Error) -> Self {
ParakeetError::IoError(err, "unknown path".to_string(), "IO operation".to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_transcription_error_detailed_helper() {
let detailed =
ParakeetError::transcription_error_detailed("bad", "decode", "buffer overflow");
match detailed {
ParakeetError::TranscriptionError(msg, stage, details) => {
assert_eq!(msg, "bad");
assert_eq!(stage, "decode");
assert_eq!(details, "buffer overflow");
}
other => panic!("Unexpected error: {other:?}"),
}
}
#[test]
fn test_chunk_processing_error_helper() {
let err = ParakeetError::chunk_processing_error("size mismatch", 1280, 2560, "EOU");
match err {
ParakeetError::ChunkProcessingError(msg, chunk_size, expected, model) => {
assert_eq!(msg, "size mismatch");
assert_eq!(chunk_size, 1280);
assert_eq!(expected, 2560);
assert_eq!(model, "EOU");
}
other => panic!("Unexpected error: {other:?}"),
}
}
#[test]
fn test_io_error_conversion() {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let parakeet_err: ParakeetError = io_err.into();
match parakeet_err {
ParakeetError::IoError(_, path, op) => {
assert_eq!(path, "unknown path");
assert_eq!(op, "IO operation");
}
other => panic!("Unexpected error: {other:?}"),
}
}
}