pub type Result<T> = core::result::Result<T, Error>;
pub use windit::WinditError;
#[derive(Debug)]
pub struct ContractMismatch {
feature: &'static str,
expected: String,
actual: String,
}
impl ContractMismatch {
#[inline(always)]
pub const fn new(feature: &'static str, expected: String, actual: String) -> Self {
Self {
feature,
expected,
actual,
}
}
#[inline(always)]
pub const fn feature(&self) -> &'static str {
self.feature
}
#[inline(always)]
pub fn expected(&self) -> &str {
&self.expected
}
#[inline(always)]
pub fn actual(&self) -> &str {
&self.actual
}
}
#[derive(Debug)]
pub struct OutputShape {
got: Vec<usize>,
expected: Vec<usize>,
}
impl OutputShape {
#[inline(always)]
pub const fn new(got: Vec<usize>, expected: Vec<usize>) -> Self {
Self { got, expected }
}
#[inline(always)]
pub fn got(&self) -> &[usize] {
&self.got
}
#[inline(always)]
pub fn expected(&self) -> &[usize] {
&self.expected
}
}
#[derive(Debug)]
pub struct AudioTooLong {
len: usize,
max: usize,
}
#[allow(clippy::len_without_is_empty)]
impl AudioTooLong {
#[inline(always)]
pub const fn new(len: usize, max: usize) -> Self {
Self { len, max }
}
#[inline(always)]
pub const fn len(&self) -> usize {
self.len
}
#[inline(always)]
pub const fn max(&self) -> usize {
self.max
}
}
#[derive(Debug)]
pub struct ClassCountMismatch {
expected: usize,
got: usize,
}
impl ClassCountMismatch {
#[inline(always)]
pub const fn new(expected: usize, got: usize) -> Self {
Self { expected, got }
}
#[inline(always)]
pub const fn expected(&self) -> usize {
self.expected
}
#[inline(always)]
pub const fn got(&self) -> usize {
self.got
}
}
#[derive(Debug)]
pub struct InvalidConfidence {
index: usize,
value: f32,
}
impl InvalidConfidence {
#[inline(always)]
pub const fn new(index: usize, value: f32) -> Self {
Self { index, value }
}
#[inline(always)]
pub const fn index(&self) -> usize {
self.index
}
#[inline(always)]
pub const fn value(&self) -> f32 {
self.value
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("failed to load model: {0}")]
Load(#[from] crate::LoadError),
#[error("prediction failed: {0}")]
Prediction(#[from] crate::PredictionError),
#[error("tensor failed: {0}")]
Tensor(#[from] crate::TensorError),
#[error(
"model contract mismatch on `{}`: expected {}, got {}",
.0.feature(),
.0.expected(),
.0.actual()
)]
ContractMismatch(ContractMismatch),
#[error("output shape mismatch: expected {:?}, got {:?}", .0.expected(), .0.got())]
OutputShape(OutputShape),
#[error(
"model declares a required input `{0}` that this door never supplies; \
it sends `mel` and nothing else, so every prediction would fail"
)]
UnsatisfiableInput(String),
#[error(
"model declares the state buffer `{0}`, and this door predicts through the \
stateless API; a stateful graph needs an `MLState` on every prediction"
)]
UnsatisfiableState(String),
#[error("model output contains a non-finite value at index {0}")]
NonFiniteOutput(usize),
#[error("audio input is empty")]
EmptyAudio,
#[error("audio input has {} samples, exceeding the fixed {}-sample window", .0.len(), .0.max())]
AudioTooLong(AudioTooLong),
#[error("audio input contains a non-finite sample at index {0}")]
NonFiniteInput(usize),
#[error("no windows to aggregate")]
EmptyWindows,
#[error(
"confidence vector has {} values, expected exactly {} (one per class)",
.0.got(),
.0.expected()
)]
ClassCountMismatch(ClassCountMismatch),
#[error(
"confidence at class index {} is {}, not a finite value in [0, 1]",
.0.index(),
.0.value()
)]
InvalidConfidence(InvalidConfidence),
#[error("windowed-sequence processing failed: {0}")]
Windowing(#[from] WinditError),
#[error("class index {0} has no rated AudioSet event")]
UnknownClassIndex(usize),
}
#[cfg(test)]
mod tests;