Skip to main content

timsrust_tdf/
frame_reader.rs

1pub(crate) mod compression1;
2pub(crate) mod compression2;
3pub(crate) mod frame_info_reader;
4
5use std::collections::HashMap;
6
7use timsrust_core::{FrameIons, utils::reader::Reader};
8
9pub(crate) use frame_info_reader::FrameInfoReader;
10
11use crate::{
12    Metadata, TDFPathError,
13    frame_reader::{
14        compression1::{
15            TdfBlobReaderCompression1, TdfBlobReaderErrorCompression1,
16        },
17        compression2::{TdfBlobReader, TdfBlobReaderError},
18    },
19};
20
21pub use frame_info_reader::FrameReaderErrorInternal;
22
23use super::{
24    MetadataReaderError, QuadrupoleSettingsReaderError, TDFPathLike,
25    file_readers::sql_reader::SqlReaderError,
26};
27
28/// Adapts a raw blob reader (indexed by binary file offset) to be indexed by
29/// frame id, using the offset table from [`FrameInfoReader`].
30#[derive(Debug)]
31struct TdfOffsetIonReader<B> {
32    blob_reader: B,
33    offsets: HashMap<usize, usize>,
34}
35
36impl<B> TdfOffsetIonReader<B> {
37    fn new(blob_reader: B, offsets: HashMap<usize, usize>) -> Self {
38        Self {
39            blob_reader,
40            offsets,
41        }
42    }
43}
44
45impl<B: Reader<FrameIons>> Reader<FrameIons> for TdfOffsetIonReader<B>
46where
47    FrameReaderError: From<B::Error>,
48{
49    type Error = FrameReaderError;
50
51    fn get(&self, index: usize) -> Result<FrameIons, Self::Error> {
52        let offset = self
53            .offsets
54            .get(&index)
55            .copied()
56            .ok_or(FrameReaderError::IndexOutOfBounds)?;
57        self.blob_reader.get(offset).map_err(FrameReaderError::from)
58    }
59}
60
61/// Unifies the two TDF compression schemes into a single [`Reader<FrameIons>`].
62#[allow(private_interfaces)]
63#[derive(Debug)]
64pub enum TdfIonReader {
65    Compression1(TdfOffsetIonReader<TdfBlobReaderCompression1>),
66    Compression2(TdfOffsetIonReader<TdfBlobReader>),
67}
68
69impl Reader<FrameIons> for TdfIonReader {
70    type Error = FrameReaderError;
71
72    fn get(&self, index: usize) -> Result<FrameIons, FrameReaderError> {
73        match self {
74            Self::Compression1(r) => r.get(index),
75            Self::Compression2(r) => r.get(index),
76        }
77    }
78}
79
80/// A concrete frame reader for Bruker TDF files.
81///
82/// Thin newtype around
83/// [`timsrust_core::FrameReader<TdfIonReader, FrameInfoReader>`].  All
84/// [`FrameReader`] methods (`get_frame`, `get_info`, `iter_indices`,
85/// `parallel_filter`, …) are available via [`Deref`].
86#[derive(Debug)]
87pub struct TdfFrameReader(
88    timsrust_core::FrameReader<TdfIonReader, FrameInfoReader>,
89);
90
91impl std::ops::Deref for TdfFrameReader {
92    type Target = timsrust_core::FrameReader<TdfIonReader, FrameInfoReader>;
93
94    fn deref(&self) -> &Self::Target {
95        &self.0
96    }
97}
98
99impl TdfFrameReader {
100    pub fn new(path: impl TDFPathLike) -> Result<Self, FrameReaderError> {
101        let metadata = Metadata::new(&path)?;
102        Self::without_metadata(
103            &path,
104            metadata.compression_type(),
105            metadata.max_peaks_per_scan(),
106        )
107    }
108
109    pub fn without_metadata(
110        path: impl TDFPathLike,
111        compression_type: u8,
112        max_peaks_per_scan: usize,
113    ) -> Result<Self, FrameReaderError> {
114        let info_reader = FrameInfoReader::new(&path)?;
115        let offsets = info_reader.offsets_map();
116        let ion_reader = match compression_type {
117            1 => {
118                let mut blob =
119                    TdfBlobReaderCompression1::new(path.to_timstof_path())?;
120                blob.set_max_peaks_per_scan(max_peaks_per_scan);
121                TdfIonReader::Compression1(TdfOffsetIonReader::new(
122                    blob, offsets,
123                ))
124            },
125            2 => {
126                let blob = TdfBlobReader::new(path.to_timstof_path())?;
127                TdfIonReader::Compression2(TdfOffsetIonReader::new(
128                    blob, offsets,
129                ))
130            },
131            _ => {
132                return Err(FrameReaderError::CompressionTypeError(
133                    compression_type,
134                ));
135            },
136        };
137        Ok(Self(timsrust_core::FrameReader::new(
138            ion_reader,
139            info_reader,
140        )))
141    }
142
143    /// Consume `self` and return the underlying generic
144    /// [`timsrust_core::FrameReader`].
145    pub fn into_inner(
146        self,
147    ) -> timsrust_core::FrameReader<TdfIonReader, FrameInfoReader> {
148        self.0
149    }
150
151    /// Return the acquisition type detected from the frame metadata.
152    pub fn get_acquisition(&self) -> timsrust_core::AcquisitionType {
153        self.info_reader().get_acquisition()
154    }
155}
156
157#[allow(private_interfaces)]
158#[derive(Debug, thiserror::Error)]
159pub enum FrameReaderError {
160    #[error("Timscompress error")]
161    TimscompressError,
162    #[error("{0}")]
163    TdfBlobReaderError(#[from] TdfBlobReaderError),
164    #[error("{0}")]
165    TdfBlobReaderErrorCompression1(#[from] TdfBlobReaderErrorCompression1),
166    #[error("{0}")]
167    MetadataReaderError(#[from] MetadataReaderError),
168    #[error("{0}")]
169    FileNotFound(String),
170    #[error("{0}")]
171    SqlReaderError(#[from] SqlReaderError),
172    #[error("Corrupt Frame")]
173    CorruptFrame,
174    #[error("{0}")]
175    QuadrupoleSettingsReaderError(#[from] QuadrupoleSettingsReaderError),
176    #[error("Index out of bounds")]
177    IndexOutOfBounds,
178    #[error("Compression type {0} not understood")]
179    CompressionTypeError(u8),
180    #[error("Failed to read path: {0}")]
181    PathError(#[from] TDFPathError),
182    #[error("Got unexpected TdfBlob type")]
183    UnexpectedTdfBlobError,
184    #[error("{0}")]
185    FrameInfoReaderError(#[from] frame_info_reader::FrameReaderErrorInternal),
186    #[error("{0}")]
187    CoreFrameReaderError(#[from] timsrust_core::FrameReaderError),
188}