use std::fmt;
use std::io;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
Io(io::Error),
Parse {
line: u64,
kind: ParseError,
},
UnknownFormat {
hint: String,
},
InvalidByte {
id: String,
pos: usize,
byte: u8,
},
LengthMismatch {
id: String,
seq: usize,
quality: usize,
},
MissingQuality {
id: String,
},
Index(String),
UnknownSequence(String),
PairMismatch {
pairs_read: u64,
first: String,
second: String,
},
PairTruncated {
pairs_read: u64,
missing: &'static str,
},
OutOfBounds {
id: String,
start: u64,
end: u64,
length: u64,
},
TooLarge {
line: u64,
what: &'static str,
limit: usize,
},
FeatureDisabled(&'static str),
Unsupported(&'static str),
Other(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ParseError {
ExpectedHeader {
found: u8,
},
ExpectedSeparator {
found: u8,
},
UnexpectedEof {
expected: &'static str,
},
EmptyId,
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParseError::ExpectedHeader { found } => {
write!(
f,
"expected a record header ('>' or '@'), found {}",
Byte(*found)
)
}
ParseError::ExpectedSeparator { found } => {
write!(
f,
"expected the FASTQ '+' separator line, found {}",
Byte(*found)
)
}
ParseError::UnexpectedEof { expected } => {
write!(f, "unexpected end of input, expected {expected}")
}
ParseError::EmptyId => write!(f, "record header does not contain an identifier"),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Io(e) => write!(f, "I/O error: {e}"),
Error::Parse { line, kind } => write!(f, "parse error on line {line}: {kind}"),
Error::UnknownFormat { hint } => {
write!(f, "cannot determine sequence format ({hint})")
}
Error::InvalidByte { id, pos, byte } => write!(
f,
"record {}: invalid residue {} at position {pos}",
Id(id),
Byte(*byte)
),
Error::LengthMismatch { id, seq, quality } => write!(
f,
"record {}: sequence has {seq} bases but quality has {quality} scores",
Id(id)
),
Error::MissingQuality { id } => {
write!(f, "record {}: FASTQ output requires quality scores", Id(id))
}
Error::Index(msg) => write!(f, "invalid FASTA index: {msg}"),
Error::UnknownSequence(name) => write!(f, "sequence {name:?} is not in the index"),
Error::PairMismatch {
pairs_read,
first,
second,
} => write!(
f,
"reads are mispaired after {pairs_read} pairs: {first:?} and {second:?} are not \
mates. Are the two files sorted the same way?"
),
Error::PairTruncated {
pairs_read,
missing,
} => write!(
f,
"{missing} ended after {pairs_read} pairs, the other did not"
),
Error::OutOfBounds {
id,
start,
end,
length,
} => write!(
f,
"region {start}..{end} is out of bounds for record {} of length {length}",
Id(id)
),
Error::TooLarge { line, what, limit } => write!(
f,
"line {line}: {what} exceeds the configured limit of {limit} bytes"
),
Error::FeatureDisabled(feature) => {
write!(
f,
"this build of fastx was compiled without the {feature:?} feature"
)
}
Error::Unsupported(what) => write!(f, "unsupported: {what}"),
Error::Other(message) => f.write_str(message),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Io(e) => Some(e),
_ => None,
}
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Error::Io(e)
}
}
impl From<Error> for io::Error {
fn from(e: Error) -> Self {
match e {
Error::Io(e) => e,
other => io::Error::new(io::ErrorKind::InvalidData, other),
}
}
}
impl Error {
pub(crate) fn parse(line: u64, kind: ParseError) -> Self {
Error::Parse { line, kind }
}
pub fn is_malformed(&self) -> bool {
!matches!(self, Error::Io(_))
}
}
struct Byte(u8);
impl fmt::Display for Byte {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
b'\n' => f.write_str("'\\n'"),
b'\r' => f.write_str("'\\r'"),
b'\t' => f.write_str("'\\t'"),
b if b.is_ascii_graphic() => write!(f, "'{}'", b as char),
b => write!(f, "0x{b:02x}"),
}
}
}
struct Id<'a>(&'a str);
impl fmt::Display for Id<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0.is_empty() {
f.write_str("<unnamed>")
} else {
write!(f, "{:?}", self.0)
}
}
}