Skip to main content

gapsmith_db/
subex.rs

1//! `dat/subex.tbl` loader.
2//!
3//! Columns: `name, metacyc, vmh, seed, group`. Maps substrate names to
4//! exchange-reaction IDs across three namespaces. Used by the transporter
5//! pipeline (`src/transporter.sh`, `src/seed_transporter.R`).
6
7use crate::common::{csv_err, io_err, DbError};
8use serde::{Deserialize, Serialize};
9use std::path::Path;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct SubexRow {
13    pub name: String,
14    #[serde(default, skip_serializing_if = "String::is_empty")]
15    pub metacyc: String,
16    #[serde(default, skip_serializing_if = "String::is_empty")]
17    pub vmh: String,
18    #[serde(default, skip_serializing_if = "String::is_empty")]
19    pub seed: String,
20    #[serde(default, skip_serializing_if = "String::is_empty")]
21    pub group: String,
22}
23
24pub fn load(path: impl AsRef<Path>) -> Result<Vec<SubexRow>, DbError> {
25    let path = path.as_ref();
26    let f = std::fs::File::open(path).map_err(|e| io_err(path, e))?;
27    let mut rdr = csv::ReaderBuilder::new()
28        .delimiter(b'\t')
29        .has_headers(true)
30        .quoting(false)
31        .flexible(true)
32        .from_reader(f);
33    let mut out = Vec::new();
34    for rec in rdr.deserialize::<SubexRow>() {
35        out.push(rec.map_err(|e| csv_err(path, e))?);
36    }
37    tracing::info!(path = %path.display(), rows = out.len(), "loaded subex");
38    Ok(out)
39}