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 mut iter = line.split_whitespace();
let symbol = iter.next()?;
let atomic_number = ATOMIC_SYMBOLS.iter().position(|&s| s == symbol)? + 1;
let x = iter.next()?.parse().ok()?;
let y = iter.next()?.parse().ok()?;
let z = iter.next()?.parse().ok()?;
*atom_count += 1;
let mut atom = Atom::new(*atom_count, atomic_number as u8, x, y, z);
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().skip(2) {
let line = line?;
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/cif.xyz", 102)]
#[case("data/mescho.xyz", 23)]
#[case("data/porphyrin.xyz", 37)]
fn test_xyz_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);
}
}