use super::parser::*;
use super::*;
fn counts_line(s: &str) -> IResult<&str, (usize, usize)> {
let mut read_count = map_res(take_s(3), |x: &str| x.trim().parse::<usize>());
do_parse!(
s,
na: read_count >> nb: read_count >> read_line >> ((na, nb))
)
}
#[test]
fn test_sdf_counts_line() {
let line = " 16 14 0 0 0 0 0 0 0 0999 V2000\n";
let (_, (na, nb)) = counts_line(line).expect("sdf counts line");
assert_eq!(16, na);
assert_eq!(14, nb);
}
fn get_atom_from(s: &str) -> IResult<&str, Atom> {
let mut read_coord = map_res(take_s(10), |x| x.trim().parse::<f64>());
let mut read_symbol = map(take_s(3), |x| x.trim());
do_parse!(
s,
x: read_coord >> y: read_coord >> z: read_coord >> s: read_symbol >> read_line >> ({
Atom::new(s, [x, y, z])
})
)
}
fn format_atom(i: usize, a: &Atom) -> String {
let pos = a.position();
format!(
"{x:-10.4} {y:-9.4} {z:-9.4} {sym:3} 0 0 0 0 0 0 0 0 0 {index:2}\n",
x = pos[0],
y = pos[1],
z = pos[2],
sym = a.symbol(),
index = i,
)
}
#[test]
fn test_sdf_atom() {
let line = " -13.5661 206.9157 111.5569 C 0 0 0 0 0 0 0 0 0 12 \n\n";
let (_, a) = get_atom_from(line).expect("sdf atom");
let line2 = format_atom(12, &a);
assert_eq!(line[..60], line2[..60]);
}
fn get_bond_from(s: &str) -> IResult<&str, (usize, usize, Bond)> {
let mut read_number = map_res(take_s(3), |x| x.trim().parse::<usize>());
do_parse!(
s,
i: read_number >> j: read_number >> b: read_number >> read_line >> ({
let bond = match b {
1 => Bond::single(),
2 => Bond::double(),
3 => Bond::triple(),
4 => Bond::aromatic(),
_ => {
warn!("ignore sdf bond type: {}", b);
Bond::single()
},
};
(i, j, bond)
})
)
}
use std::fmt::Display;
fn format_bond<T: Display>(index1: T, index2: T, bond: &Bond) -> String {
format!(
"{index1:>3}{index2:>3}{order:3} 0 0 0 \n",
index1 = index1,
index2 = index2,
order = 1
)
}
#[test]
fn test_sdf_bond() {
let line = " 6 7 1 0 0 0 \n";
let (_, (index1, index2, bond)) = get_bond_from(line).expect("sdf bond");
let line2 = format_bond(index1, index2, &bond);
assert_eq!(line[..9], line2[..9]);
}
pub fn get_molecule_from(input: &str) -> IResult<&str, Molecule> {
let mut read_atoms = many1(get_atom_from);
let mut read_bonds = many0(get_bond_from);
let (input, mol) = do_parse!(
input,
title : read_line >> software: read_line >> comment : read_line >> counts : counts_line >> atoms : read_atoms >> bonds : read_bonds >> (
{
let naa = atoms.len();
let nbb = bonds.len();
let (na, nb) = counts;
if na != naa {
eprintln!("expect {} atoms, but found {}", na, naa);
}
if nb != nbb {
eprintln!("expect {} bonds, but found {}", nb, nbb);
}
let mut mol = Molecule::from_atoms(atoms);
mol.set_title(title);
for (i, j, b) in bonds {
mol.add_bond(i, j, b);
}
mol
}
)
)?;
Ok((input, mol))
}
fn format_molecule(mol: &Molecule) -> String {
let mut lines = String::new();
lines.push_str(&format!("{}\n", mol.title()));
lines.push_str("gchemol\n");
lines.push_str("\n");
let line = format!(
"{natoms:3}{nbonds:3} 0 0 0 0 0 0 0 0999 V2000 \n",
natoms = mol.natoms(),
nbonds = mol.nbonds()
);
lines.push_str(&line);
for (i, a) in mol.atoms() {
lines.push_str(&format_atom(i, a));
}
for (i, j, b) in mol.bonds() {
lines.push_str(&format_bond(i, j, &b));
}
lines.push_str("M END\n$$$$\n");
lines
}
#[derive(Clone, Copy, Debug)]
pub struct SdfFile();
impl ChemicalFile for SdfFile {
fn ftype(&self) -> &str {
"text/sdf"
}
fn possible_extensions(&self) -> Vec<&str> {
vec![".sd", ".sdf", ".mol"]
}
fn format_molecule(&self, mol: &Molecule) -> Result<String> {
if mol.lattice.is_some() {
eprintln!("WARNING: cannot render Lattice in SDF format!");
}
Ok(format_molecule(mol))
}
}
impl ParseMolecule for SdfFile {
fn parse_molecule(&self, input: &str) -> Result<Molecule> {
let (_, mol) = get_molecule_from(input).map_err(|e| format_err!("parse SDF format failure: {:?}", e))?;
Ok(mol)
}
}
impl ReadPart for SdfFile {
fn read_next(&self, context: ReadContext) -> ReadAction {
Terminated(|line: &str| line == "$$$$\n").read_next(context)
}
}
impl SdfFile {
pub fn partitions<R: BufRead + Seek>(&self, mut r: TextReader<R>) -> Result<impl Iterator<Item = String>> {
Ok(r.partitions(*self))
}
}