use crate::parser::Format;
use std::error::Error as StdError;
use std::fmt;
use std::io;
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ErrorPosition {
pub line: u64,
pub id: Option<String>,
}
impl fmt::Display for ErrorPosition {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(id) = self.id.as_ref() {
write!(f, "record '{id}' at ")?;
}
write!(f, "line {}", self.line)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ParseErrorKind {
Io,
UnknownFormat,
InvalidStart,
InvalidSeparator,
UnequalLengths,
UnexpectedEnd,
EmptyFile,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ParseError {
pub msg: String,
pub kind: ParseErrorKind,
pub position: ErrorPosition,
pub format: Option<Format>,
}
impl ParseError {
pub fn new_invalid_start(byte_found: u8, position: ErrorPosition, format: Format) -> Self {
let msg = format!(
"Expected '{}' but found '{}",
format.start_char(),
(byte_found as char).escape_default()
);
Self {
kind: ParseErrorKind::InvalidStart,
msg,
position,
format: Some(format),
}
}
pub fn new_invalid_separator(byte_found: u8, position: ErrorPosition) -> Self {
let msg = format!(
"Expected '+' separator but found '{}",
(byte_found as char).escape_default()
);
Self {
kind: ParseErrorKind::InvalidSeparator,
msg,
position,
format: Some(Format::Fastq),
}
}
pub fn new_unknown_format(byte_found: u8) -> Self {
let msg = format!(
"Expected '@' or '>' at the start of the file but found '{}'.",
(byte_found as char).escape_default()
);
Self {
kind: ParseErrorKind::UnknownFormat,
msg,
position: ErrorPosition::default(),
format: Some(Format::Fastq),
}
}
pub fn new_unequal_length(seq_len: usize, qual_len: usize, position: ErrorPosition) -> Self {
let msg = format!("Sequence length is {seq_len} but quality length is {qual_len}");
Self {
kind: ParseErrorKind::UnequalLengths,
msg,
position,
format: Some(Format::Fastq),
}
}
pub fn new_unexpected_end(position: ErrorPosition, format: Format) -> Self {
Self {
msg: String::new(),
kind: ParseErrorKind::UnexpectedEnd,
position,
format: Some(format),
}
}
pub fn new_empty_file() -> Self {
Self {
msg: String::from("Failed to read the first two bytes. Is the file empty?"),
kind: ParseErrorKind::EmptyFile,
position: ErrorPosition::default(),
format: None,
}
}
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.kind {
ParseErrorKind::Io => write!(f, "I/O error: {}", self.msg),
ParseErrorKind::UnequalLengths
| ParseErrorKind::InvalidStart
| ParseErrorKind::UnknownFormat
| ParseErrorKind::EmptyFile
| ParseErrorKind::InvalidSeparator => write!(f, "{} ({})", self.msg, self.position),
ParseErrorKind::UnexpectedEnd => {
write!(f, "Unexpected end of input ({}).", self.position)
}
}
}
}
impl From<io::Error> for ParseError {
fn from(err: io::Error) -> Self {
Self {
msg: err.to_string(),
kind: ParseErrorKind::Io,
position: ErrorPosition::default(),
format: None,
}
}
}
impl StdError for ParseError {
fn cause(&self) -> Option<&dyn StdError> {
None
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct PhredOffsetError {
pub q: u8,
pub offset: u8,
}
impl fmt::Display for PhredOffsetError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"character '{}' cannot be decoded with offset '{}'",
self.q as char, self.offset
)
}
}