use crate::{atom::{Atom, AtomStr, ATOM_NAME_EXPECT, ATOM_RESNAME_EXPECT, ATOM_TYPE_NAME_EXPECT}, io::FileFormatHandler};
use std::{
fs::File,
io::{BufRead, BufReader},
num::{ParseFloatError, ParseIntError},
path::Path,
};
use thiserror::Error;
use super::Topology;
use regex;
pub struct ItpFileHandler {
file: File,
already_read: bool,
}
impl FileFormatHandler for ItpFileHandler {
fn open(fname: impl AsRef<Path>) -> Result<Self, super::FileFormatError>
where
Self: Sized,
{
Ok(Self {
file: File::open(fname).map_err(|e| ItpHandlerError::OpenRead(e))?,
already_read: false,
})
}
fn read_topology(&mut self) -> Result<Topology, super::FileFormatError> {
if self.already_read {
return Err(super::FileFormatError::Eof);
}
let mut top = Topology::default();
let mut reader = BufReader::new(&self.file);
let mut line = String::new();
let moltype_re = regex::Regex::new(r"\[\s+moleculetype\s+\]").unwrap();
loop {
line.clear();
let nread = reader.read_line(&mut line).unwrap();
if nread == 0 {
return Err(ItpHandlerError::NoMoleculetype)?;
}
if moltype_re.is_match(&line) {
break;
}
}
let atoms_re = regex::Regex::new(r"\[\s+atoms\s+\]").unwrap();
loop {
line.clear();
let nread = reader.read_line(&mut line).unwrap();
if nread == 0 {
return Err(ItpHandlerError::NoAtoms)?;
}
if atoms_re.is_match(&line) {
break;
}
}
loop {
line.clear();
let nread = reader.read_line(&mut line).unwrap();
let l = line.trim();
if nread == 0 {
break;
}
if l.trim_start().starts_with(";") || l.len() == 0 {
continue;
}
let fields = l.split_ascii_whitespace().collect::<Vec<_>>();
if fields.len() < 8 {
break;
}
let mut at = Atom {
name: AtomStr::try_from_str(fields[4]).expect(ATOM_NAME_EXPECT),
resname: AtomStr::try_from_str(fields[3]).expect(ATOM_RESNAME_EXPECT),
type_name: AtomStr::try_from_str(fields[1]).expect(ATOM_TYPE_NAME_EXPECT),
resid: fields[2].parse().map_err(ItpHandlerError::ParseInt)?,
charge: fields[6].parse().map_err(ItpHandlerError::ParseFloat)?,
mass: fields[7].parse().map_err(ItpHandlerError::ParseFloat)?,
..Default::default()
};
at.guess_element_from_name();
top.atoms.push(at);
}
Ok(top.into())
}
}
#[derive(Debug, Error)]
pub enum ItpHandlerError {
#[error("can't open itp file for reading")]
OpenRead(#[source] std::io::Error),
#[error("no moleculetype found")]
NoMoleculetype,
#[error("no atoms found")]
NoAtoms,
#[error(transparent)]
Regex(#[from] regex::Error),
#[error(transparent)]
ParseInt(#[from] ParseIntError),
#[error(transparent)]
ParseFloat(#[from] ParseFloatError),
}