timsrust 0.4.2

A crate to read Bruker timsTOF data
Documentation
use crate::{
    io::readers::{
        file_readers::sql_reader::{
            pasef_frame_msms::SqlPasefFrameMsMs, ReadableSqlTable, SqlReader,
            SqlReaderError,
        },
        FrameReader, FrameReaderError,
    },
    utils::vec_utils::{argsort, group_and_sum},
};

use super::raw_spectra::{
    RawSpectrum, RawSpectrumReaderError, RawSpectrumReaderTrait,
};

#[derive(Debug)]
pub struct DDARawSpectrumReader {
    order: Vec<usize>,
    offsets: Vec<usize>,
    pasef_frames: Vec<SqlPasefFrameMsMs>,
    frame_reader: FrameReader,
}

impl DDARawSpectrumReader {
    pub fn new(
        tdf_sql_reader: &SqlReader,
        frame_reader: FrameReader,
    ) -> Result<Self, DDARawSpectrumReaderError> {
        let pasef_frames = SqlPasefFrameMsMs::from_sql_reader(&tdf_sql_reader)?;
        let pasef_precursors =
            &pasef_frames.iter().map(|x| x.precursor).collect();
        let order: Vec<usize> = argsort(&pasef_precursors);
        let max_precursor = pasef_precursors
            .iter()
            .max()
            .expect("SqlReader cannot return empty vecs, so there is always a max precursor index");
        let mut offsets: Vec<usize> = Vec::with_capacity(max_precursor + 1);
        offsets.push(0);
        for (offset, &index) in order.iter().enumerate().take(order.len() - 1) {
            let second_index: usize = order[offset + 1];
            if pasef_precursors[index] != pasef_precursors[second_index] {
                offsets.push(offset + 1)
            }
        }
        offsets.push(order.len());
        let reader = Self {
            order,
            offsets,
            pasef_frames,
            frame_reader,
        };
        Ok(reader)
    }

    pub fn iterate_over_pasef_frames(
        &self,
        index: usize,
    ) -> impl Iterator<Item = &SqlPasefFrameMsMs> {
        let start: usize = self.offsets[index];
        let end: usize = self.offsets[index + 1];
        self.order[start..end]
            .iter()
            .map(|&x| &self.pasef_frames[x])
    }

    fn _get(
        &self,
        index: usize,
    ) -> Result<RawSpectrum, DDARawSpectrumReaderError> {
        let mut collision_energy = 0.0;
        let mut isolation_mz = 0.0;
        let mut isolation_width = 0.0;
        let mut tof_indices: Vec<u32> = vec![];
        let mut intensities: Vec<u32> = vec![];
        for pasef_frame in self.iterate_over_pasef_frames(index) {
            collision_energy = pasef_frame.collision_energy;
            isolation_mz = pasef_frame.isolation_mz;
            isolation_width = pasef_frame.isolation_width;
            let frame_index: usize = pasef_frame.frame - 1;
            let frame = self.frame_reader.get(frame_index)?;
            if frame.intensities.len() == 0 {
                continue;
            }
            let scan_start: usize = pasef_frame.scan_start;
            let scan_end: usize = pasef_frame.scan_end;
            let offset_start: usize = frame.scan_offsets[scan_start] as usize;
            let offset_end: usize = frame.scan_offsets[scan_end] as usize;
            let tof_selection: &[u32] =
                &frame.tof_indices[offset_start..offset_end];
            let intensity_selection: &[u32] =
                &frame.intensities[offset_start..offset_end];
            tof_indices.extend(tof_selection);
            intensities.extend(intensity_selection);
        }
        let (raw_tof_indices, raw_intensities) = group_and_sum(
            tof_indices,
            intensities.iter().map(|x| *x as u64).collect(),
        );
        let raw_spectrum = RawSpectrum {
            tof_indices: raw_tof_indices,
            intensities: raw_intensities,
            index: index,
            collision_energy,
            isolation_mz,
            isolation_width,
        };
        Ok(raw_spectrum)
    }
}

impl RawSpectrumReaderTrait for DDARawSpectrumReader {
    fn get(&self, index: usize) -> Result<RawSpectrum, RawSpectrumReaderError> {
        Ok(self._get(index)?)
    }

    fn len(&self) -> usize {
        self.offsets.len() - 1
    }
}

#[derive(Debug, thiserror::Error)]
pub enum DDARawSpectrumReaderError {
    #[error("{0}")]
    SqlReaderError(#[from] SqlReaderError),
    #[error("{0}")]
    FrameReaderError(#[from] FrameReaderError),
}