use alloc::{string::String, vec::Vec};
use thiserror::Error;
pub use parse_error::{HeaderSection, ParseError};
pub use warning::Warning;
mod parse_error;
mod warning;
#[non_exhaustive]
#[derive(Debug, Error)]
pub enum BiopacError {
#[error("{0}")]
Parse(#[from] ParseError),
#[cfg(feature = "std")]
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("{0}")]
Compression(#[from] CompressionError),
#[error("{0}")]
UnsupportedVersion(#[from] UnsupportedVersionError),
#[error("invalid channel: {0}")]
InvalidChannel(String),
#[error("validation error: {0}")]
Validation(String),
#[cfg(any(feature = "arrow", feature = "parquet"))]
#[error("arrow error: {0}")]
Arrow(#[from] arrow_schema::ArrowError),
#[cfg(feature = "parquet")]
#[error("parquet error: {0}")]
Parquet(#[from] parquet::errors::ParquetError),
}
const _: fn() = || {
const fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<BiopacError>();
};
#[derive(Debug, Error)]
#[error("compression error in channel {channel_index}: {message}")]
pub struct CompressionError {
pub channel_index: u16,
pub message: String,
}
#[derive(Debug, Error)]
#[error(
"unsupported file revision {revision} \
(supported range: {min_supported}..={max_supported})"
)]
pub struct UnsupportedVersionError {
pub revision: i32,
pub min_supported: i32,
pub max_supported: i32,
}
#[derive(Debug)]
pub struct ParseResult<T> {
pub value: T,
pub warnings: Vec<Warning>,
}
impl<T> ParseResult<T> {
pub const fn ok(value: T) -> Self {
Self {
value,
warnings: Vec::new(),
}
}
pub const fn is_clean(&self) -> bool {
self.warnings.is_empty()
}
pub fn into_value(self) -> T {
self.value
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compression_error_display() {
let e = CompressionError {
channel_index: 2,
message: String::from("unexpected end of stream"),
};
let s = alloc::format!("{e}");
assert!(s.contains("channel 2"));
assert!(s.contains("unexpected end of stream"));
}
#[test]
fn unsupported_version_display() {
let e = UnsupportedVersionError {
revision: 25,
min_supported: 30,
max_supported: 84,
};
let s = alloc::format!("{e}");
assert!(s.contains("25"));
assert!(s.contains("30"));
assert!(s.contains("84"));
}
#[test]
fn all_variants_are_constructable() {
let _p = BiopacError::Parse(ParseError {
byte_offset: 0x1A3C,
expected: String::from("nChanHeaderLen >= 252"),
actual: String::from("180"),
section: HeaderSection::Channel(2),
});
let _c = BiopacError::Compression(CompressionError {
channel_index: 0,
message: String::from("test"),
});
drop(BiopacError::UnsupportedVersion(UnsupportedVersionError {
revision: 20,
min_supported: 30,
max_supported: 84,
}));
let _i = BiopacError::InvalidChannel(String::from("out of range"));
let _v = BiopacError::Validation(String::from("inconsistent channel count"));
}
#[test]
fn parse_result_ok_is_clean() {
let r: ParseResult<u32> = ParseResult::ok(42);
assert!(r.is_clean());
assert_eq!(r.into_value(), 42);
}
}