timsrust 0.4.2

A crate to read Bruker timsTOF data
Documentation
pub mod frame_groups;
pub mod frames;
pub mod metadata;
pub mod pasef_frame_msms;
pub mod precursors;
pub mod quad_settings;

use std::collections::HashMap;

use rusqlite::{types::FromSql, Connection};

use crate::readers::{TimsTofPathError, TimsTofPathLike};

#[derive(Debug)]
pub struct SqlReader {
    connection: Connection,
}

impl SqlReader {
    pub fn open(path: impl TimsTofPathLike) -> Result<Self, SqlReaderError> {
        let path = path.to_timstof_path()?;
        let connection = Connection::open(&path.tdf()?)?;
        Ok(Self { connection })
    }

    pub fn read_column_from_table<T: rusqlite::types::FromSql + Default>(
        &self,
        column_name: &str,
        table_name: &str,
    ) -> Result<Vec<T>, SqlReaderError> {
        let query = format!("SELECT {} FROM {}", column_name, table_name);
        let mut stmt = self.connection.prepare(&query)?;
        let rows = stmt.query_map([], |row| match row.get::<usize, T>(0) {
            Ok(value) => Ok(value),
            _ => Ok(T::default()),
        })?;
        let result = rows.collect::<Result<Vec<_>, _>>()?;
        Ok(result)
    }
}

pub trait ReadableSqlTable {
    fn get_sql_query() -> String;

    fn from_sql_row(row: &rusqlite::Row) -> Self;

    fn from_sql_reader(reader: &SqlReader) -> Result<Vec<Self>, SqlReaderError>
    where
        Self: Sized,
    {
        let query = Self::get_sql_query();
        let mut stmt = reader.connection.prepare(&query)?;
        let rows = stmt.query_map([], |row| Ok(Self::from_sql_row(row)))?;
        let result = rows.collect::<Result<Vec<_>, _>>()?;
        if result.len() == 0 {
            Err(SqlReaderError::SqlError(
                rusqlite::Error::QueryReturnedNoRows,
            ))
        } else {
            Ok(result)
        }
    }
}

pub trait ReadableSqlHashMap {
    fn get_sql_query() -> String;

    fn from_sql_reader(
        reader: &SqlReader,
    ) -> Result<HashMap<String, String>, SqlReaderError>
    where
        Self: Sized,
    {
        let query = Self::get_sql_query();
        let mut stmt = reader.connection.prepare(&query)?;
        let mut result = HashMap::new();
        let rows = stmt.query_map([], |row| {
            let key: String = row.get(0)?;
            let value: String = row.get(1)?;
            result.insert(key, value);
            Ok(())
        })?;
        rows.collect::<Result<Vec<_>, _>>()?;
        Ok(result)
    }
}

pub trait ParseDefault {
    fn parse_default<T: Default + FromSql>(&self, index: usize) -> T;
}

impl ParseDefault for rusqlite::Row<'_> {
    fn parse_default<T: Default + FromSql>(&self, index: usize) -> T {
        self.get(index).unwrap_or_default()
    }
}

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