use super::DbOrf;
use std::collections::HashMap;
use std::path::Path;
pub fn ingest_isosdb(
fna: &Path,
fam_annot: &Path,
) -> Result<(Vec<DbOrf>, Vec<(String, Vec<u8>)>), String> {
let elem_fam = parse_fam_annot(fam_annot)?; let elems = super::read_fasta(fna)?; let meta = rustygal::meta_api::meta_bins();
let mut out: Vec<DbOrf> = Vec::new();
let mut nt_cds: Vec<(String, Vec<u8>)> = Vec::new();
for (i, (elem_id, dna)) in elems.iter().enumerate() {
let res = rustygal::meta_api::run_meta(i as i32 + 1, elem_id, dna, &meta);
let family = elem_fam
.get(elem_id)
.cloned()
.unwrap_or_else(|| "unknown".to_string());
let prot_orfs = parse_trans_faa(&res.trans_faa, elem_id);
let nt_by_idx = parse_nuc_faa(&res.nuc, elem_id);
for (gene_idx, prot) in prot_orfs {
let id = format!("{elem_id}_orf{gene_idx}");
if let Some(nt) = nt_by_idx.get(&gene_idx) {
nt_cds.push((id.clone(), nt.clone()));
}
out.push(DbOrf {
id,
family: family.clone(),
source: "ISOSDB",
seq: prot,
});
}
}
Ok((out, nt_cds))
}
fn parse_trans_faa(data: &[u8], elem_id: &str) -> Vec<(u32, Vec<u8>)> {
let text = String::from_utf8_lossy(data);
let mut out: Vec<(u32, Vec<u8>)> = Vec::new();
let mut idx: Option<u32> = None;
let mut seq: Vec<u8> = Vec::new();
let prefix = format!("{elem_id}_");
for line in text.lines() {
if let Some(hdr) = line.strip_prefix('>') {
if let Some(k) = idx.take() {
push_orf(&mut out, k, std::mem::take(&mut seq));
}
seq.clear();
let tok = hdr.split_whitespace().next().unwrap_or("");
idx = tok
.strip_prefix(&prefix)
.and_then(|s| s.parse::<u32>().ok());
} else if idx.is_some() {
seq.extend(line.trim().bytes().filter(|&b| b != b'\r' && b != b'*'));
}
}
if let Some(k) = idx.take() {
push_orf(&mut out, k, seq);
}
out
}
fn parse_nuc_faa(data: &[u8], elem_id: &str) -> HashMap<u32, Vec<u8>> {
let text = String::from_utf8_lossy(data);
let mut out: HashMap<u32, Vec<u8>> = HashMap::new();
let mut idx: Option<u32> = None;
let mut seq: Vec<u8> = Vec::new();
let prefix = format!("{elem_id}_");
for line in text.lines() {
if let Some(hdr) = line.strip_prefix('>') {
if let Some(k) = idx.take() {
out.insert(k, std::mem::take(&mut seq));
}
seq.clear();
let tok = hdr.split_whitespace().next().unwrap_or("");
idx = tok
.strip_prefix(&prefix)
.and_then(|s| s.parse::<u32>().ok());
} else if idx.is_some() {
seq.extend(line.trim().bytes().filter(|&b| b != b'\r'));
}
}
if let Some(k) = idx.take() {
out.insert(k, seq);
}
out
}
const MIN_AA: usize = 30;
fn push_orf(out: &mut Vec<(u32, Vec<u8>)>, k: u32, seq: Vec<u8>) {
if seq.len() >= MIN_AA {
out.push((k, seq));
}
}
fn parse_fam_annot(path: &Path) -> Result<HashMap<String, String>, String> {
let data = std::fs::read_to_string(path).map_err(|e| format!("read {}: {e}", path.display()))?;
let mut map = HashMap::new();
for line in data.lines() {
let mut it = line.split('\t');
let (Some(elem), Some(fam)) = (it.next(), it.next()) else {
continue;
};
let (elem, fam) = (elem.trim(), fam.trim());
if elem.is_empty() || elem == "IS" {
continue; }
map.insert(elem.to_string(), fam.to_string());
}
Ok(map)
}