use std::{fs, io, path::Path};
use na_seq::Element;
#[derive(Debug)]
pub struct WildAtom {
pub name: String, pub elements: Vec<String>, }
#[derive(Debug)]
pub struct AtomTypeDef {
pub name: String,
pub residue: Option<String>,
pub element: Option<Element>,
pub attached_atoms: Option<u8>,
pub attached_h: Option<u8>, pub electron_withdrawal_count: Option<u8>,
pub atomic_property: Option<String>,
pub chem_env: Option<String>,
}
fn parse_u8_field(s: &str) -> Option<u8> {
if s == "*" { None } else { s.parse().ok() }
}
fn parse_str_field(s: Option<&str>) -> Option<String> {
match s {
None => None,
Some("*") => None,
Some(v) => Some(v.to_string()),
}
}
#[derive(Debug)]
pub struct AmberDef {
pub wildatoms: Vec<WildAtom>,
pub atomtypes: Vec<AtomTypeDef>,
}
impl AmberDef {
pub fn new(text: &str) -> io::Result<Self> {
let mut wildatoms = Vec::new();
let mut atomtypes = Vec::new();
for raw_line in text.lines() {
let line = raw_line.trim();
if line.is_empty() {
continue;
}
if line.starts_with('#') || line.starts_with("//") {
continue;
}
if line.starts_with('=') || line.starts_with('-') {
continue;
}
let mut tokens: Vec<&str> = line.split_whitespace().collect();
if tokens.is_empty() {
continue;
}
if tokens.last() == Some(&"&") {
tokens.pop();
}
match tokens[0] {
"WILDATOM" => {
if tokens.len() >= 3 {
let name = tokens[1].to_string();
let elements = tokens[2..].iter().map(|s| (*s).to_string()).collect();
wildatoms.push(WildAtom { name, elements });
}
}
"ATD" => {
if tokens.len() < 2 {
continue;
}
let name = tokens[1].to_string();
let residue = parse_str_field(tokens.get(2).copied());
let atomic_number = tokens.get(3).and_then(|s| parse_u8_field(s));
let element = atomic_number
.map(|n| Element::from_atomic_number(n).unwrap_or(Element::Zinc));
let attached_atoms = tokens.get(4).and_then(|s| parse_u8_field(s));
let attached_h = tokens.get(5).and_then(|s| parse_u8_field(s));
let ew_count = tokens.get(6).and_then(|s| parse_u8_field(s));
let atomic_property = parse_str_field(tokens.get(7).copied());
let env = parse_str_field(tokens.get(8).copied());
atomtypes.push(AtomTypeDef {
name,
residue,
element,
attached_atoms,
attached_h,
electron_withdrawal_count: ew_count,
atomic_property,
chem_env: env,
});
}
_ => {}
}
}
Ok(Self {
wildatoms,
atomtypes,
})
}
pub fn load(path: &Path) -> io::Result<Self> {
let data_str = fs::read_to_string(path)?;
Self::new(&data_str)
}
}