use std::error::Error as StdError;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum Error {
#[error("Error processing header: {0}")]
HeaderError(#[from] HeaderError),
#[error("Error processing CBQ: {0}")]
CbqError(#[from] CbqError),
#[error("Error writing file: {0}")]
WriteError(#[from] WriteError),
#[error("Error reading file: {0}")]
ReadError(#[from] ReadError),
#[error("Error processing Index: {0}")]
IndexError(#[from] IndexError),
#[error("Error with IO: {0}")]
IoError(#[from] std::io::Error),
#[error("Error with UTF8: {0}")]
Utf8Error(#[from] std::str::Utf8Error),
#[error("Error determining BINSEQ format: {0}")]
FormatError(#[from] FormatError),
#[error("Bitnuc error: {0}")]
BitnucError(Box<dyn StdError + Send + Sync>),
#[cfg(feature = "anyhow")]
#[error("Generic error: {0}")]
AnyhowError(#[from] anyhow::Error),
#[error("Generic error: {0}")]
GenericError(#[from] Box<dyn StdError + Send + Sync>),
}
impl From<bitnuc_deprec::Error> for Error {
fn from(err: bitnuc_deprec::Error) -> Self {
Self::BitnucError(Box::new(err))
}
}
impl From<bitnuc::BitnucError> for Error {
fn from(err: bitnuc::BitnucError) -> Self {
Self::BitnucError(Box::new(err))
}
}
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum HeaderError {
#[error("Invalid magic number: {0}")]
InvalidMagicNumber(u32),
#[error("Invalid format version: {0}")]
InvalidFormatVersion(u8),
#[error("Invalid reserved bytes")]
InvalidReservedBytes,
#[error("Invalid bit size found in header: {0} - expecting [2,4]")]
InvalidBitSize(u8),
#[error("Invalid number of bytes provided: {0}. Expected: {1}")]
InvalidSize(usize, usize),
#[error("Missing sequence length")]
MissingSequenceLength,
}
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum ReadError {
#[error("File is not regular")]
IncompatibleFile,
#[error(
"Number of bytes in file does not match expectation - possibly truncated at byte pos {0}"
)]
FileTruncation(usize),
#[error("Requested record index ({requested_index}) is out of record range ({max_index})")]
OutOfRange {
requested_index: usize,
max_index: usize,
},
#[error("Invalid range specified: start ({start}) is greater than end ({end})")]
InvalidRange { start: usize, end: usize },
#[error("End of stream reached")]
EndOfStream,
#[error("Partial record at end of stream ({0} bytes)")]
PartialRecord(usize),
#[error("Unexpected Block Magic Number found: {0} at position {1}")]
InvalidBlockMagicNumber(u64, usize),
#[error("Unable to find an expected full block at position {0}")]
UnexpectedEndOfFile(usize),
#[error("Unexpected file metadata")]
InvalidFileType,
#[error("Missing index end magic number")]
MissingIndexEndMagic,
}
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum WriteError {
#[error(
"Cannot push record ({attribute}: {actual}) with writer configuration ({attribute}: {expected})"
)]
ConfigurationMismatch {
attribute: &'static str,
expected: bool,
actual: bool,
},
#[error("Cannot ingest writer with incompatible formats")]
FormatMismatch,
#[error(
"Missing required sequence length, expected (primary: {exp_primary}, extended: {exp_extended}), got (primary: {obs_primary}, extended: {obs_extended})"
)]
MissingSequenceLength {
exp_primary: bool,
exp_extended: bool,
obs_primary: bool,
obs_extended: bool,
},
#[error("Sequence length ({got}) does not match the header ({expected})")]
UnexpectedSequenceLength { expected: u32, got: usize },
#[error("Invalid nucleotides found in sequence: {0}")]
InvalidNucleotideSequence(String),
#[error("Missing header in writer builder")]
MissingHeader,
#[error(
"Encountered a record with embedded size {0} but the maximum block size is {1}. Rerun with increased block size."
)]
RecordSizeExceedsMaximumBlockSize(usize, usize),
#[error(
"Incompatible block sizes encountered in BlockWriter Ingest. Found ({1}) Expected ({0})"
)]
IncompatibleBlockSizes(usize, usize),
#[error("Incompatible headers found in vbq::Writer::ingest. Found ({1:?}) Expected ({0:?})")]
IncompatibleHeaders(crate::vbq::FileHeader, crate::vbq::FileHeader),
#[error("SequencingRecordBuilder requires a primary sequence (s_seq)")]
MissingSequence,
#[cfg(feature = "paraseq")]
#[error("Empty FASTX file")]
EmptyFastxFile,
#[cfg(feature = "paraseq")]
#[error("Builder not provided with any input")]
MissingInput,
}
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum IndexError {
#[error("Invalid magic number: {0}")]
InvalidMagicNumber(u64),
#[error("Invalid reserved bytes in index header")]
InvalidReservedBytes,
}
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum CbqError {
#[error(
"Record size ({record_size}) exceeds maximum block size ({max_block_size}) - Try increasing block size."
)]
ExceedsMaximumBlockSize {
max_block_size: usize,
record_size: usize,
},
#[error("Cannot ingest block of size {other_block_size} into block of size {self_block_size}")]
CannotIngestBlock {
self_block_size: usize,
other_block_size: usize,
},
#[error(
"Block(size: {block_size}) will be exceeded by record size {record_size}. Current size: {current_size}"
)]
BlockFull {
current_size: usize,
record_size: usize,
block_size: usize,
},
#[error("Invalid block header MAGIC found")]
InvalidBlockHeaderMagic,
#[error("Invalid file header MAGIC found")]
InvalidFileHeaderMagic,
#[error("Unsupported CBQ file version {found} (this library supports up to {supported})")]
UnsupportedFileVersion { found: u8, supported: u8 },
#[error("Unsupported CBQ block version {found} (this library supports up to {supported})")]
UnsupportedBlockVersion { found: u8, supported: u8 },
#[error("Invalid index header MAGIC found")]
InvalidIndexHeaderMagic,
#[error("Invalid index footer MAGIC found")]
InvalidIndexFooterMagic,
#[error("Unable to cast bytes to Index - likely an alignment error")]
IndexCastingError,
}
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum FormatError {
#[error("Unable to determine BINSEQ format from magic bytes in file: {0}")]
UnrecognizedMagicBytes(String),
}
pub trait IntoBinseqError {
fn into_binseq_error(self) -> Error;
}
impl<E> IntoBinseqError for E
where
E: StdError + Send + Sync + 'static,
{
fn into_binseq_error(self) -> Error {
Error::GenericError(Box::new(self))
}
}
#[cfg(test)]
mod testing {
use super::*;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum MyError {
#[error("Custom error: {0}")]
CustomError(String),
}
#[test]
fn test_into_binseq_error() {
let my_error = MyError::CustomError(String::from("some error"));
let binseq_error = my_error.into_binseq_error();
assert!(matches!(binseq_error, Error::GenericError(_)));
}
#[test]
fn test_error_from_sub_error() {
let error: Error = HeaderError::InvalidMagicNumber(0x1234).into();
assert!(matches!(error, Error::HeaderError(_)));
}
}