chiral-db 0.2.1

ChiralDB: a cheminformatics database written in Rust
Documentation
//! Substructure Searching
//! 
//! 

use crate::types;
use crate::config;

pub type SmilesDB = std::collections::HashMap<String, std::sync::Arc<SMILESDocument>>;

///
/// SMILESDocument
/// 

#[derive(Debug)]
pub struct SMILESDocument {
    ids: Vec<types::ID>,
    data: Vec<types::SMILES>,
}

impl SMILESDocument {
    pub fn len(&self) -> usize { self.ids.len() }
    pub fn get_id(&self, idx: usize) -> &types::ID { &self.ids[idx] }
    pub fn get_data(&self, idx: usize) -> &types::SMILES { &self.data[idx] }

    pub fn desc(&self) -> String {
        vec![
            format!("{}", self.len()),
        ].join("\t\t")
    }

    pub fn new_from_smiles_vec(smiles_vec: &Vec<&String>, ids_input: &Vec<String>) -> Self {
        let ids = ids_input.to_vec();
        let data: Vec<types::SMILES> = smiles_vec.iter()
            .map(|s| s.to_string())
            .collect();

        Self { ids, data }
    }

    pub fn new_from_chembl(filepath: &std::path::Path) -> Self {
        let sc = chiral_db_sources::chembl::SourceChembl::new(filepath);
        let (smiles_vec, ids) = sc.get_smiles_id_pairs();
        SMILESDocument::new_from_smiles_vec(&smiles_vec, &ids) 
    }

    pub fn get_doc_ob_molecule(&self) -> Vec<(types::ID, types::SMILES)> {
        (0..(self.len()))
            .map(|idx| (
                self.get_id(idx).to_string(),
                self.get_data(idx).to_string()
            ))
            .collect()
    }
}

///
/// Substructure Search
/// 

fn ob_substructure_match_mol(smarts: &types::SMARTS, smiles: &types::SMILES) -> types::SubstructureMatch {
    let sp = openbabel::smartspattern::SmartsPattern::new_from_smarts(smarts);
    let mol = openbabel::molecule::Molecule::new_from_smiles(smiles);
    // utils::cxx_vector_into_vector(&sp.find_match(&mol))
    sp.find_match(&mol)
}

fn ob_substructure_match_doc(smarts: &types::SMARTS, doc_mol: &Vec<(types::ID, types::SMILES)>) -> types::ResultSubstructure {
    doc_mol.iter()
        .map(|(id, smiles)| (id.to_string(), ob_substructure_match_mol(smarts, smiles)))
        .filter(|(_id, m)| m.len() > 0)
        .collect()
}

pub fn query_substructure(toolkit: &types::Toolkit, smarts_query: &types::SMARTS, doc_smiles: &SMILESDocument) -> types::ResultSubstructure {
    match toolkit {
        types::Toolkit::OpenBabel => {
            let doc_mol = doc_smiles.get_doc_ob_molecule();
            ob_substructure_match_doc(smarts_query, &doc_mol)
        }
    }
}

///
/// Data Loading
/// 

fn load_smiles_chembl(smiles_doc: &config::SmilesDocumentConfig) -> SMILESDocument {
    let filepath = std::path::Path::new(&smiles_doc.filepath);
    SMILESDocument::new_from_chembl(&filepath)
}

pub fn load_smiles_db(conf: &config::Config) -> SmilesDB {
    let mut smiles_db = SmilesDB::new();
    if let Some(smiles_doc) = &conf.smiles_doc {
        for smiles_doc in smiles_doc.iter() {
            match &smiles_doc.source {
                types::Source::Chembl => {
                    smiles_db.insert(smiles_doc.name.clone(), std::sync::Arc::new(load_smiles_chembl(smiles_doc)));
                }
                types::Source::Zinc => {
                    unimplemented!();
                }
            }
        }
    }

    smiles_db
}

#[cfg(test)]
mod test_substructure {
    use super::*;

    #[test]
    fn test_smiels_doc_new_from_smiles_vec() {
        let filepath = std::path::Path::new("../chiral-db-example-data/ChEMBL/chembl_30_chemreps_100.txt");
        let sc = chiral_db_sources::chembl::SourceChembl::new(&filepath);
        let (smiles_vec, ids) = sc.get_smiles_id_pairs();
        let sd = SMILESDocument::new_from_smiles_vec(&smiles_vec, &ids);
        assert_eq!(sd.len(), 100);
    }

    #[test]
    fn test_query_substructure() {
        let filepath = std::path::Path::new("../chiral-db-example-data/ChEMBL/chembl_30_chemreps_10k.txt");
        let sc = chiral_db_sources::chembl::SourceChembl::new(&filepath);
        let (smiles_vec, ids) = sc.get_smiles_id_pairs();
        let sd = SMILESDocument::new_from_smiles_vec(&smiles_vec, &ids);

        let smarts_1 = String::from("Cc1cc(NC(=O)c2cc(Cl)cc(Cl)c2O)ccc1Sc1nc2ccccc2s1");
        let res_1 = query_substructure(&types::Toolkit::OpenBabel, &smarts_1, &sd);
        assert_eq!(res_1.len(), 1);
        assert!(res_1.contains_key("CHEMBL263810"));
        let smarts_2 = String::from("Sc1nc2ccccc2s1");
        let res_2 = query_substructure(&types::Toolkit::OpenBabel, &smarts_2, &sd);
        assert_eq!(res_2.len(), 2);
        assert!(res_2.contains_key("CHEMBL6685"));
        assert!(res_2.contains_key("CHEMBL263810"));
    }
}