use std::time::Duration;
use smol_str::SmolStr;
use crate::types::{ChunkId, Lang};
#[derive(Clone, Debug, thiserror::Error)]
pub enum TranscriberError {
#[error("{0}")]
PtsRegression(PtsRegression),
#[error("{0}")]
GapExceedsTolerance(GapExceedsTolerance),
#[error("{0}")]
Backpressure(Backpressure),
#[error("handle_vad_segment called before any handle_samples")]
OutputTimebaseUnset,
#[error("{0}")]
VadAheadOfAudio(VadAheadOfAudio),
#[error("{0}")]
InconsistentTimebase(InconsistentTimebase),
#[error("{0}")]
InvalidTimebase(InvalidTimebase),
#[error("unknown or already-resolved chunk_id {0}")]
UnknownChunk(ChunkId),
#[error("operation rejected after handle_eof")]
AfterEof,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
#[error("PTS regression on {kind:?}: advance = {advance}")]
pub struct PtsRegression {
kind: PushKind,
advance: i64,
}
impl PtsRegression {
#[must_use]
pub const fn new(kind: PushKind, advance: i64) -> Self {
Self { kind, advance }
}
#[must_use]
pub const fn kind(&self) -> PushKind {
self.kind
}
#[must_use]
pub const fn advance(&self) -> i64 {
self.advance
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
#[error("forward gap {gap_samples} samples exceeds tolerance {tolerance_samples}")]
pub struct GapExceedsTolerance {
gap_samples: u64,
tolerance_samples: u64,
}
impl GapExceedsTolerance {
#[must_use]
pub const fn new(gap_samples: u64, tolerance_samples: u64) -> Self {
Self {
gap_samples,
tolerance_samples,
}
}
#[must_use]
pub const fn gap_samples(&self) -> u64 {
self.gap_samples
}
#[must_use]
pub const fn tolerance_samples(&self) -> u64 {
self.tolerance_samples
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
#[error("sample buffer at capacity ({buffered}/{cap})")]
pub struct Backpressure {
buffered: usize,
cap: usize,
}
impl Backpressure {
#[must_use]
pub const fn new(buffered: usize, cap: usize) -> Self {
Self { buffered, cap }
}
#[must_use]
pub const fn buffered(&self) -> usize {
self.buffered
}
#[must_use]
pub const fn cap(&self) -> usize {
self.cap
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
#[error("VAD segment end {vad_end} is past buffered samples {buffered}")]
pub struct VadAheadOfAudio {
vad_end: u64,
buffered: u64,
}
impl VadAheadOfAudio {
#[must_use]
pub const fn new(vad_end: u64, buffered: u64) -> Self {
Self { vad_end, buffered }
}
#[must_use]
pub const fn vad_end(&self) -> u64 {
self.vad_end
}
#[must_use]
pub const fn buffered(&self) -> u64 {
self.buffered
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
#[error("inconsistent output timebase: expected {expected:?}, got {got:?}")]
pub struct InconsistentTimebase {
expected: mediatime::Timebase,
got: mediatime::Timebase,
}
impl InconsistentTimebase {
#[must_use]
pub const fn new(expected: mediatime::Timebase, got: mediatime::Timebase) -> Self {
Self { expected, got }
}
#[must_use]
pub const fn expected(&self) -> mediatime::Timebase {
self.expected
}
#[must_use]
pub const fn got(&self) -> mediatime::Timebase {
self.got
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
#[error("timebase numerator must be non-zero (got {numerator})")]
pub struct InvalidTimebase {
numerator: u32,
}
impl InvalidTimebase {
#[must_use]
pub const fn new(numerator: u32) -> Self {
Self { numerator }
}
#[must_use]
pub const fn numerator(&self) -> u32 {
self.numerator
}
}
#[derive(Clone, Debug, thiserror::Error)]
pub enum WorkFailure {
#[error(transparent)]
Asr(AsrError),
#[error(transparent)]
Alignment(AlignmentError),
#[error("{0}")]
LanguageUnsupported(LanguageUnsupportedForAlignment),
#[error("{0}")]
WorkerHang(WorkerHangTimeout),
}
#[derive(Clone, Debug, thiserror::Error)]
pub enum AsrError {
#[error("ASR all temperatures failed: {0}")]
AllTemperaturesExhausted(AsrFailure),
#[error("ASR unsupported language: {0}")]
UnsupportedLanguage(AsrFailure),
#[error("ASR backend error: {0}")]
Backend(AsrFailure),
}
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
#[error("{message}")]
pub struct AsrFailure {
message: SmolStr,
}
impl AsrFailure {
#[must_use]
pub const fn new(message: SmolStr) -> Self {
Self { message }
}
#[must_use]
pub fn message(&self) -> &SmolStr {
&self.message
}
}
#[derive(Clone, Debug, thiserror::Error)]
#[non_exhaustive]
pub enum AlignmentError {
#[error("alignment model inference failed: {0}")]
ModelInference(AlignmentFailure),
#[error("alignment tokenization failed: {0}")]
Tokenization(AlignmentFailure),
#[error("alignment normalization failed: {0}")]
Normalization(AlignmentFailure),
#[error("no alignment path: {0}")]
NoAlignmentPath(AlignmentFailure),
#[error("empty text after normalisation: {0}")]
EmptyText(AlignmentFailure),
#[error("alignment semantic-OOV: {0}")]
SemanticOutOfVocab(AlignmentFailure),
#[error("alignment aborted before completing: {0}")]
Aborted(AlignmentFailure),
}
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
#[error("language={language:?} message={message}")]
pub struct AlignmentFailure {
message: SmolStr,
language: Lang,
}
impl AlignmentFailure {
#[must_use]
pub const fn new(message: SmolStr, language: Lang) -> Self {
Self { message, language }
}
#[must_use]
pub fn message(&self) -> &SmolStr {
&self.message
}
#[must_use]
pub const fn language(&self) -> &Lang {
&self.language
}
}
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
#[error("no aligner registered for language {language:?}")]
pub struct LanguageUnsupportedForAlignment {
language: Lang,
}
impl LanguageUnsupportedForAlignment {
#[must_use]
pub const fn new(language: Lang) -> Self {
Self { language }
}
#[must_use]
pub const fn language(&self) -> &Lang {
&self.language
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
#[error("{kind:?} worker hung; elapsed {elapsed:?}")]
pub struct WorkerHangTimeout {
kind: WorkerKind,
elapsed: Duration,
}
impl WorkerHangTimeout {
#[must_use]
pub const fn new(kind: WorkerKind, elapsed: Duration) -> Self {
Self { kind, elapsed }
}
#[must_use]
pub const fn kind(&self) -> WorkerKind {
self.kind
}
#[must_use]
pub const fn elapsed(&self) -> Duration {
self.elapsed
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum PushKind {
Samples,
VadSegment,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum WorkerKind {
Asr,
Alignment,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pts_regression_displays_kind() {
let e = TranscriberError::PtsRegression(PtsRegression::new(PushKind::Samples, -100));
let s = e.to_string();
assert!(s.contains("Samples"));
assert!(s.contains("-100"));
}
#[test]
fn work_failure_clones() {
let f = WorkFailure::Asr(AsrError::AllTemperaturesExhausted(AsrFailure::new(
"oops".into(),
)));
let _ = f.clone();
}
}