use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum KmeRustError {
#[error("invalid k-mer length {k}: must be between {min} and {max}")]
InvalidKmerLength { k: usize, min: u8, max: u8 },
#[error("invalid base '{base}' at position {position}")]
InvalidBase { base: u8, position: usize },
#[error("failed to read sequence file '{path}': {source}")]
SequenceRead {
#[source]
source: std::io::Error,
path: PathBuf,
},
#[error("failed to parse sequence record: {details}")]
SequenceParse { details: String },
#[error("failed to write output: {source}")]
WriteError {
#[source]
source: std::io::Error,
},
#[error("failed to serialize JSON: {source}")]
JsonError {
#[source]
source: serde_json::Error,
},
#[cfg(feature = "gzip")]
#[error("failed to decompress gzip file '{path}': {source}")]
GzipError {
#[source]
source: std::io::Error,
path: PathBuf,
},
#[cfg(feature = "mmap")]
#[error("failed to memory-map file '{path}': {source}")]
MmapError {
#[source]
source: std::io::Error,
path: PathBuf,
},
#[error("failed to read index file '{path}': {source}")]
IndexRead {
#[source]
source: std::io::Error,
path: PathBuf,
},
#[error("failed to write index file '{path}': {source}")]
IndexWrite {
#[source]
source: std::io::Error,
path: PathBuf,
},
#[error("invalid index file '{path}': {details}")]
InvalidIndex { details: String, path: PathBuf },
}
#[derive(Debug, Clone, Error, PartialEq, Eq)]
#[error("k-mer length {k} is out of range: must be between {min} and {max}")]
pub struct KmerLengthError {
pub k: usize,
pub min: u8,
pub max: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InvalidBaseError {
pub base: u8,
pub position: usize,
}
impl std::fmt::Display for InvalidBaseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.base.is_ascii_graphic() || self.base == b' ' {
write!(
f,
"invalid base '{}' (0x{:02x}) at position {}",
self.base as char, self.base, self.position
)
} else {
write!(
f,
"invalid base 0x{:02x} at position {}",
self.base, self.position
)
}
}
}
impl std::error::Error for InvalidBaseError {}
impl From<std::io::Error> for KmeRustError {
fn from(source: std::io::Error) -> Self {
Self::WriteError { source }
}
}
impl From<serde_json::Error> for KmeRustError {
fn from(source: serde_json::Error) -> Self {
Self::JsonError { source }
}
}
impl From<KmerLengthError> for KmeRustError {
fn from(err: KmerLengthError) -> Self {
Self::InvalidKmerLength {
k: err.k,
min: err.min,
max: err.max,
}
}
}
impl From<InvalidBaseError> for KmeRustError {
fn from(err: InvalidBaseError) -> Self {
Self::InvalidBase {
base: err.base,
position: err.position,
}
}
}
#[derive(Debug, Error)]
pub enum BuilderError {
#[error("k-mer length not set; call .k() first")]
KmerLengthNotSet,
#[error(transparent)]
KmerLength(#[from] KmerLengthError),
#[error(transparent)]
Kmerust(#[from] KmeRustError),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("{0}")]
Process(String),
}
impl From<Box<dyn std::error::Error>> for BuilderError {
fn from(err: Box<dyn std::error::Error>) -> Self {
Self::Process(err.to_string())
}
}
impl From<crate::run::ProcessError> for BuilderError {
fn from(err: crate::run::ProcessError) -> Self {
Self::Process(err.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn kmer_length_error_display() {
let err = KmerLengthError {
k: 50,
min: 1,
max: 32,
};
assert_eq!(
err.to_string(),
"k-mer length 50 is out of range: must be between 1 and 32"
);
}
#[test]
fn invalid_base_error_display() {
let err = InvalidBaseError {
base: b'N',
position: 5,
};
assert_eq!(err.to_string(), "invalid base 'N' (0x4e) at position 5");
}
#[test]
fn kmerust_error_from_kmer_length_error() {
let err: KmeRustError = KmerLengthError {
k: 0,
min: 1,
max: 32,
}
.into();
assert!(matches!(err, KmeRustError::InvalidKmerLength { k: 0, .. }));
}
#[test]
fn kmerust_error_from_invalid_base_error() {
let err: KmeRustError = InvalidBaseError {
base: b'X',
position: 3,
}
.into();
assert!(matches!(
err,
KmeRustError::InvalidBase {
base: b'X',
position: 3
}
));
}
}