Skip to main content

dino_seq/
error.rs

1use std::fmt;
2
3/// Crate-local result type.
4pub type Result<T> = std::result::Result<T, FastqError>;
5
6/// Location of a FASTQ or FASTA parsing error.
7///
8/// `byte_offset` is absolute within the original byte stream. `record_index`
9/// is zero-based. `line_index` is `0` for header, `1` for sequence, `2` for
10/// plus, and `3` for quality.
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct FastqPosition {
13    /// Absolute byte offset in the original input stream.
14    pub byte_offset: u64,
15    /// Zero-based FASTQ record index.
16    pub record_index: u64,
17    /// Zero-based line within the four-line FASTQ record.
18    pub line_index: u8,
19}
20
21impl FastqPosition {
22    /// Create a new position.
23    pub const fn new(byte_offset: u64, record_index: u64, line_index: u8) -> Self {
24        Self {
25            byte_offset,
26            record_index,
27            line_index,
28        }
29    }
30}
31
32impl fmt::Display for FastqPosition {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        write!(
35            f,
36            "byte {}, record {}, line {}",
37            self.byte_offset, self.record_index, self.line_index
38        )
39    }
40}
41
42/// Error type for FASTQ, FASTA, gzip, and BGZF operations.
43#[derive(Debug)]
44#[non_exhaustive]
45pub enum FastqError {
46    /// I/O error from the underlying reader or file.
47    Io(std::io::Error),
48    /// Format error without a precise byte position.
49    Format(String),
50    /// Format error with byte, record, and line position.
51    FormatAt {
52        /// Human-readable format error.
53        message: String,
54        /// Position of the error.
55        position: FastqPosition,
56    },
57    /// BGZF-specific error.
58    Bgzf(String),
59    /// A single record did not fit in the configured slab.
60    RecordTooLarge {
61        /// Configured slab size in bytes.
62        slab_size: usize,
63    },
64}
65
66impl fmt::Display for FastqError {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        match self {
69            Self::Io(err) => write!(f, "I/O error: {err}"),
70            Self::Format(msg) => write!(f, "parse error: {msg}"),
71            Self::FormatAt { message, position } => {
72                write!(f, "parse error at {position}: {message}")
73            }
74            Self::Bgzf(msg) => write!(f, "BGZF error: {msg}"),
75            Self::RecordTooLarge { slab_size } => {
76                write!(f, "record exceeds slab size ({slab_size} bytes)")
77            }
78        }
79    }
80}
81
82impl std::error::Error for FastqError {
83    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
84        match self {
85            Self::Io(err) => Some(err),
86            Self::Format(_)
87            | Self::FormatAt { .. }
88            | Self::Bgzf(_)
89            | Self::RecordTooLarge { .. } => None,
90        }
91    }
92}
93
94impl From<std::io::Error> for FastqError {
95    fn from(value: std::io::Error) -> Self {
96        Self::Io(value)
97    }
98}