use crate::result::Transcript;
use transcribe_cpp_sys as sys;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("invalid argument: {0}")]
InvalidArgument(String),
#[error("not implemented by this model: {0}")]
NotImplemented(String),
#[error("model file not found: {0}")]
ModelFileNotFound(String),
#[error("model load failed: {0}")]
ModelLoad(String),
#[error("out of memory: {0}")]
OutOfMemory(String),
#[error("backend error: {0}")]
Backend(String),
#[error("unsupported request: {0}")]
Unsupported(String),
#[error("ABI struct-size mismatch: {0}")]
BadStructSize(String),
#[error("input too long: {0}")]
InputTooLong(String),
#[error("operation aborted: {message}")]
Aborted {
message: String,
partial: Option<Box<Transcript>>,
},
#[error("output truncated before end-of-stream: {message}")]
OutputTruncated {
message: String,
partial: Option<Box<Transcript>>,
},
#[error("native library version mismatch: {0}")]
VersionMismatch(String),
#[error("string contains an interior NUL byte: {0}")]
Nul(#[from] std::ffi::NulError),
#[error("model busy: {0}")]
Busy(String),
#[error("{0}")]
Other(String),
}
impl Error {
pub fn raw_status(&self) -> i32 {
use sys::transcribe_status as S;
let s = match self {
Error::InvalidArgument(_) => S::TRANSCRIBE_ERR_INVALID_ARG,
Error::NotImplemented(_) => S::TRANSCRIBE_ERR_NOT_IMPLEMENTED,
Error::ModelFileNotFound(_) => S::TRANSCRIBE_ERR_FILE_NOT_FOUND,
Error::ModelLoad(_) => S::TRANSCRIBE_ERR_GGUF,
Error::OutOfMemory(_) => S::TRANSCRIBE_ERR_OOM,
Error::Backend(_) => S::TRANSCRIBE_ERR_BACKEND,
Error::Unsupported(_) => S::TRANSCRIBE_ERR_UNSUPPORTED_TASK,
Error::BadStructSize(_) => S::TRANSCRIBE_ERR_BAD_STRUCT_SIZE,
Error::InputTooLong(_) => S::TRANSCRIBE_ERR_INPUT_TOO_LONG,
Error::Aborted { .. } => S::TRANSCRIBE_ERR_ABORTED,
Error::OutputTruncated { .. } => S::TRANSCRIBE_ERR_OUTPUT_TRUNCATED,
_ => S::TRANSCRIBE_OK,
};
s.0 as i32
}
pub fn partial(&self) -> Option<&Transcript> {
match self {
Error::Aborted { partial, .. } | Error::OutputTruncated { partial, .. } => {
partial.as_deref()
}
_ => None,
}
}
}
pub(crate) fn status_string(status: i32) -> String {
let ptr = unsafe { sys::transcribe_status_string(status) };
if ptr.is_null() {
return format!("status {status}");
}
unsafe { std::ffi::CStr::from_ptr(ptr) }
.to_string_lossy()
.into_owned()
}
pub(crate) fn error_for_status(status: sys::transcribe_status, context: &str) -> Error {
use sys::transcribe_status as S;
let code = status.0 as i32;
let msg = {
let s = status_string(code);
if context.is_empty() {
format!("{s} (status {code})")
} else {
format!("{context}: {s} (status {code})")
}
};
match status {
S::TRANSCRIBE_ERR_INVALID_ARG | S::TRANSCRIBE_ERR_SAMPLE_RATE => {
Error::InvalidArgument(msg)
}
S::TRANSCRIBE_ERR_NOT_IMPLEMENTED => Error::NotImplemented(msg),
S::TRANSCRIBE_ERR_FILE_NOT_FOUND => Error::ModelFileNotFound(msg),
S::TRANSCRIBE_ERR_GGUF
| S::TRANSCRIBE_ERR_UNSUPPORTED_ARCH
| S::TRANSCRIBE_ERR_UNSUPPORTED_VARIANT => Error::ModelLoad(msg),
S::TRANSCRIBE_ERR_OOM => Error::OutOfMemory(msg),
S::TRANSCRIBE_ERR_BACKEND => Error::Backend(msg),
S::TRANSCRIBE_ERR_UNSUPPORTED_LANGUAGE
| S::TRANSCRIBE_ERR_UNSUPPORTED_TASK
| S::TRANSCRIBE_ERR_UNSUPPORTED_TIMESTAMPS
| S::TRANSCRIBE_ERR_UNSUPPORTED_PNC
| S::TRANSCRIBE_ERR_UNSUPPORTED_ITN => Error::Unsupported(msg),
S::TRANSCRIBE_ERR_BAD_STRUCT_SIZE => Error::BadStructSize(msg),
S::TRANSCRIBE_ERR_INPUT_TOO_LONG => Error::InputTooLong(msg),
S::TRANSCRIBE_ERR_ABORTED => Error::Aborted {
message: msg,
partial: None,
},
S::TRANSCRIBE_ERR_OUTPUT_TRUNCATED => Error::OutputTruncated {
message: msg,
partial: None,
},
_ => Error::Other(msg),
}
}
pub(crate) fn check(status: sys::transcribe_status, context: &str) -> Result<()> {
if status == sys::transcribe_status::TRANSCRIBE_OK {
Ok(())
} else {
Err(error_for_status(status, context))
}
}