use std::error::Error;
use audio_samples::utils::audio_math::note_to_midi;
use audio_samples::{
AudioSampleError, ChannelRequirement, ConversionError, EnumParseError, FeatureError,
LayoutError, ParameterError, ProcessingError, SampleType,
};
use miette::{Diagnostic, Report};
fn main() {
section("1. Source spans — the caret points at what's wrong");
spans_demo();
section("2. Inspecting a diagnostic programmatically");
inspect_demo();
section("3. Matching on the *structured* taxonomy (no string parsing)");
taxonomy_demo();
section("4. Preserved cause chains (foreign errors kept as sources)");
cause_chain_demo();
section("5. A gallery across every error domain");
gallery();
}
fn spans_demo() {
if let Err(e) = note_to_midi("H4") {
render(e);
}
if let Err(e) = note_to_midi("C#x") {
render(e);
}
render(EnumParseError::new("PadSide", "middle", &["left", "right", "both"]).into());
}
fn inspect_demo() {
let err: AudioSampleError = ParameterError::out_of_range(
"cutoff_hz",
25_000,
20,
22_050,
"exceeds the Nyquist limit for 44.1 kHz audio",
)
.into();
inspect(&err);
let parse = EnumParseError::new("WindowType", "hann", &["hanning", "hamming", "blackman"]);
println!(" labels on EnumParseError(\"hann\"):");
if let Some(labels) = parse.labels() {
for label in labels {
println!(
" • offset {}, len {}{}",
label.offset(),
label.len(),
label
.label()
.map(|t| format!(" — \"{t}\""))
.unwrap_or_default(),
);
}
}
}
fn inspect(err: &AudioSampleError) {
println!(" display : {err}");
print_opt(" code :", err.code());
print_opt(" severity :", err.severity().map(|s| format!("{s:?}")));
print_opt(" help :", err.help());
print_opt(" docs url :", err.url());
}
fn taxonomy_demo() {
let layout = LayoutError::channel_count_unsupported("stft", ChannelRequirement::Mono, 2);
match &layout {
LayoutError::ChannelCountUnsupported {
operation,
required,
actual,
..
} => {
println!(" `{operation}` needs {required}, got {actual} channel(s)");
if matches!(required, ChannelRequirement::Mono) {
println!(" → caller can react: down-mix with `.to_mono()` and retry");
}
}
_ => unreachable!(),
}
render(AudioSampleError::from(layout));
let conv = ConversionError::audio_conversion(
1.5_f32,
SampleType::F32,
SampleType::I16,
"value lies outside the normalised [-1.0, 1.0] range",
);
if let ConversionError::AudioConversion { from, to, .. } = &conv {
println!(" conversion failed: {from} → {to} (typed, not strings)");
}
render(AudioSampleError::from(conv));
}
fn cause_chain_demo() {
let shape_err = ndarray::Array2::<f32>::from_shape_vec((2, 3), vec![0.0; 5])
.expect_err("a 2x3 array needs 6 elements, so this must fail");
let err: AudioSampleError = LayoutError::shape_error("reshape_to_stereo", shape_err).into();
println!(" walking the cause chain:");
let mut current: Option<&dyn Error> = Some(&err);
let mut depth = 0;
while let Some(e) = current {
println!(" {}{e}", " ".repeat(depth));
current = e.source();
depth += 1;
}
render(err);
}
fn gallery() {
let errors: Vec<AudioSampleError> = vec![
ParameterError::missing("sample_rate").into(),
LayoutError::borrowed_mutation("normalize_in_place", "audio is borrowed immutably").into(),
ProcessingError::algorithm_failure("butterworth", "filter became unstable at this order")
.into(),
ConversionError::numeric_cast(
300.0_f64,
SampleType::F64,
SampleType::U8,
"value exceeds the 0..=255 range of u8",
)
.into(),
FeatureError::not_enabled("transforms", "stft").into(),
AudioSampleError::empty_data("rms_envelope"),
AudioSampleError::unsupported("band_stop_butterworth", "not implemented for order > 8"),
];
for err in errors {
let code = err
.code()
.map(|c| c.to_string())
.unwrap_or_else(|| "<none>".into());
println!(" [{code}]");
println!(" {err}");
if let Some(help) = err.help() {
println!(" help: {help}");
}
println!();
}
}
fn render(err: AudioSampleError) {
let report: Report = err.into();
println!("{report:?}\n");
}
fn section(title: &str) {
println!("\n\x1b[1m{title}\x1b[0m");
println!("{}", "─".repeat(title.len()));
}
fn print_opt(label: &str, value: Option<impl std::fmt::Display>) {
match value {
Some(v) => println!("{label} {v}"),
None => println!("{label} <none>"),
}
}