chelate/
lib.rs

1use std::{
2    ffi::OsStr,
3    fs::File,
4    io::{self, BufReader, Read},
5    path::Path,
6    vec,
7};
8
9pub mod cif;
10pub mod mol;
11pub mod mol2;
12pub mod pdb;
13pub mod xyz;
14
15/// Parses a file based on the FileType and returns a vector of `Atom` and a vector of `Bond` objects.
16/// # Examples
17/// ```
18/// use chelate;
19/// let (atoms, bonds) = chelate::from_file("data/147288.cif").unwrap();
20///
21/// assert_eq!(atoms.len(), 206);
22/// assert_eq!(bonds.len(), 230);
23/// ```
24pub fn from_file(filename: impl AsRef<Path>) -> io::Result<(Vec<Atom>, Vec<Bond>)> {
25    let file = File::open(&filename)?;
26    let reader = BufReader::new(file);
27
28    match filename.as_ref().extension().and_then(OsStr::to_str) {
29        Some("cif") => parse(reader, FileType::CIF),
30        Some("mol") => parse(reader, FileType::MOL),
31        Some("mol2") => parse(reader, FileType::MOL2),
32        Some("pdb") => parse(reader, FileType::PDB),
33        Some("xyz") => parse(reader, FileType::XYZ),
34        _ => Err(io::Error::new(
35            io::ErrorKind::InvalidInput,
36            "Unsupported file extension",
37        )),
38    }
39}
40
41/// Enum to declare one of the supported chemical filetypes
42pub enum FileType {
43    CIF,
44    MOL,
45    MOL2,
46    PDB,
47    XYZ,
48}
49
50/// Parses a file based on the FileType and returns a vector of `Atom` and a vector of `Bond` objects.
51/// # Examples
52/// ```
53/// use chelate;
54/// use chelate::FileType;
55/// use std::fs::File;
56/// use std::io::BufReader;
57///
58/// let file = File::open("data/147288.cif").unwrap();
59/// let reader = BufReader::new(file);
60/// let (atoms, bonds) = chelate::parse(reader, FileType::CIF).unwrap();
61///
62/// assert_eq!(atoms.len(), 206);
63/// assert_eq!(bonds.len(), 230);
64/// ```
65pub fn parse<P: Read>(reader: BufReader<P>, type_: FileType) -> io::Result<(Vec<Atom>, Vec<Bond>)> {
66    match type_ {
67        FileType::CIF => cif::parse(reader),
68        FileType::MOL => mol::parse(reader),
69        FileType::MOL2 => mol2::parse(reader),
70        FileType::PDB => Ok((pdb::parse(reader)?, vec![])),
71        FileType::XYZ => Ok((xyz::parse(reader)?, vec![])),
72    }
73}
74
75pub(crate) fn normalize_symbol(symbol: &str) -> String {
76    let normalized_symbol = if let Some(first_char) = symbol.chars().next() {
77        first_char.to_uppercase().collect::<String>() + &symbol[1..].to_lowercase()
78    } else {
79        String::new()
80    };
81    normalized_symbol
82}
83
84#[derive(Debug)]
85pub struct Atom {
86    pub id: usize,
87    pub atomic_number: u8,
88    pub is_disordered: bool,
89    pub coord: [f32; 3],
90}
91
92impl Atom {
93    pub fn new(id: usize, atomic_number: u8, x: f32, y: f32, z: f32) -> Self {
94        Atom {
95            id,
96            is_disordered: false,
97            atomic_number,
98            coord: [x, y, z],
99        }
100    }
101}
102
103static ATOMIC_SYMBOLS: [&str; 118] = [
104    "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P", "S", "Cl",
105    "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", "As",
106    "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In",
107    "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd", "Pm", "Sm", "Eu", "Gd", "Tb",
108    "Dy", "Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl",
109    "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th", "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk",
110    "Cf", "Es", "Fm", "Md", "No", "Lr", "Rf", "Db", "Sg", "Bh", "Hs", "Mt", "Ds", "Rg", "Cn", "Nh",
111    "Fl", "Mc", "Lv", "Ts", "Og",
112];
113
114#[derive(Debug)]
115pub struct Bond {
116    pub atom1: usize,
117    pub atom2: usize,
118    pub order: u8,
119    pub is_aromatic: bool,
120}
121
122impl Bond {
123    pub fn new(atom1: Atom, atom2: Atom, order: u8, is_aromatic: bool) -> Self {
124        Bond {
125            atom1: atom1.id,
126            atom2: atom2.id,
127            order,
128            is_aromatic,
129        }
130    }
131}