use super::normalize_symbol;
use crate::atom::{ATOMIC_SYMBOLS, Atom};
use std::io::{self, BufRead, BufReader, Read};
fn parse_atom_line(line: &str, atom_count: &mut usize) -> Option<Atom> {
let symbol = line[76..78].trim();
let atomic_number = ATOMIC_SYMBOLS
.iter()
.position(|&s| s == normalize_symbol(symbol))?
+ 1;
let x = line[30..38].trim().parse().ok()?;
let y = line[38..46].trim().parse().ok()?;
let z = line[46..54].trim().parse().ok()?;
let chain = line[21..22].trim().parse().unwrap_or_default();
let resname = line[17..20].to_string();
let resid = line[22..26].trim().parse().unwrap_or_default();
let occ = line[54..60].trim().parse().unwrap_or(1.0);
*atom_count += 1;
let mut atom = Atom::new(*atom_count, atomic_number as u8, x, y, z);
atom.chain = chain;
atom.resname = resname;
atom.resid = resid;
atom.occupancy = occ;
atom.name = symbol.to_string();
Some(atom)
}
pub fn parse<P: Read>(reader: BufReader<P>) -> io::Result<Vec<Atom>> {
let mut atom_count = 0;
let mut atoms = Vec::new();
for line in reader.lines() {
let line = line?;
if !line.starts_with("ATOM") && !line.starts_with("HETATM") {
continue;
}
if let Some(atom) = parse_atom_line(&line, &mut atom_count) {
atoms.push(atom);
}
}
Ok(atoms)
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
use std::fs::File;
#[rstest]
#[case("data/oriluy.pdb", 130)]
#[case("data/2spl.pdb", 1437)]
#[case("data/1hv4.pdb", 9288)]
#[case("data/0001.pdb", 15450)]
fn test_pdb_files(#[case] filename: &str, #[case] len: usize) {
let file = File::open(filename).unwrap();
let reader = BufReader::new(file);
let atoms = parse(reader).unwrap();
assert_eq!(atoms.len(), len);
}
}