use crate::types::{SampleRate, Word};
pub trait Asr: Send + Sync {
fn transcribe(&self, audio: &[f32], sample_rate: SampleRate) -> Result<Vec<Word>, AsrError>;
}
#[derive(Debug, thiserror::Error)]
pub enum AsrError {
#[error("unsupported sample rate: {actual} Hz")]
UnsupportedSampleRate { actual: u32 },
#[error("ASR inference failed: {detail}")]
InferenceFailed { detail: String },
#[error("model file io error on {path}: {detail}")]
ModelIo {
path: std::path::PathBuf,
detail: String,
},
#[error("ASR backend error: {0}")]
Backend(String),
}
#[allow(clippy::unwrap_used)]
#[cfg(test)]
mod tests {
use super::*;
use crate::types::TimeRange;
struct MockAsr;
impl Asr for MockAsr {
fn transcribe(
&self,
_audio: &[f32],
_sample_rate: SampleRate,
) -> Result<Vec<Word>, AsrError> {
Ok(vec![Word {
word: "hi".to_owned(),
time: TimeRange {
start: 0.0,
end: 0.5,
},
confidence: 0.9,
}])
}
}
#[test]
fn asr_is_object_safe() {
let boxed: Box<dyn Asr> = Box::new(MockAsr);
let sr = SampleRate::new(16000).unwrap();
let words = boxed.transcribe(&[0.0_f32; 16], sr).unwrap();
assert_eq!(words.len(), 1);
assert_eq!(words[0].word, "hi");
}
#[test]
fn asr_error_displays() {
assert!(
AsrError::UnsupportedSampleRate { actual: 8000 }
.to_string()
.contains("8000")
);
}
}