knuckles-parse 0.2.2

A tooklkit for parsing PDB records
Documentation
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

#[cfg(feature = "python")]
use pyo3::prelude::*;

#[cfg(feature = "python")]
use knuckles_macro::pydefault;

#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "python", pyclass(get_all, set_all))]
#[cfg_attr(feature = "python", pydefault)]
pub struct DBRefRecord {
    pub id_code: String,
    pub chain_id: char,
    pub seq_begin: u32,
    pub insert_begin: Option<char>,
    pub seq_end: u32,
    pub insert_end: Option<char>,
    pub database: DBType,
    pub db_accession: String,
    pub db_id_code: String,
    pub db_seq_begin: u32,
    pub i_dbns_beg: Option<char>,
    pub db_seq_end: u32,
    pub db_ins_end: Option<char>,
}

#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "python", pyclass(eq, eq_int))]
#[cfg_attr(feature = "python", pydefault)]
pub enum DBType {
    GB,
    NORINE,
    PDB,
    UNP,
}

impl DBType {
    pub fn new(str: &str) -> Self {
        match str.trim() {
            "GB" => DBType::GB,
            "NORINE" | "NOR" => DBType::NORINE,
            "PDB" => DBType::PDB,
            "UNP" => DBType::UNP,
            _ => panic!("Unknown DBType"),
        }
    }
}

impl DBRefRecord {
    pub fn new(str: &str) -> Self {
        let database = DBType::new(&str[26..32]);
        let mut i_dbns_beg: Option<char> = None;
        let mut db_ins_end: Option<char> = None;
        if let DBType::PDB = database {
            i_dbns_beg = Some(str.chars().nth(60).unwrap());
            db_ins_end = Some(str.chars().nth(67).unwrap());
        }
        DBRefRecord {
            id_code: str[7..11].trim().to_string(),
            chain_id: str.chars().nth(12).unwrap(),
            seq_begin: str[14..18].trim().parse().unwrap(),
            insert_begin: str[18..19].trim().parse().ok(),
            seq_end: str[20..24].trim().parse().unwrap(),
            insert_end: str[24..25].trim().parse().ok(),
            database,
            db_accession: str[33..41].trim().to_string(),
            db_id_code: str[42..54].trim().to_string(),
            db_seq_begin: str[55..60].trim().parse().unwrap(),
            i_dbns_beg,
            db_seq_end: str[62..67].trim().parse().unwrap(),
            db_ins_end,
        }
    }
}

impl From<&str> for DBRefRecord {
    fn from(line: &str) -> Self {
        Self::new(line)
    }
}

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

    #[test]
    fn test_new_dbref() {
        let line = "DBREF  2JHQ A    1   226  UNP    Q9KPK8   UNG_VIBCH        1    226";
        let record = DBRefRecord::new(line);
        assert_eq!("2JHQ", record.id_code);
        assert_eq!('A', record.chain_id);
        assert_eq!(1, record.seq_begin);
        assert_eq!(None, record.insert_begin);
        assert_eq!(226, record.seq_end);
        assert_eq!(None, record.insert_end);
        assert_eq!(DBType::UNP, record.database);
        assert_eq!("Q9KPK8", record.db_accession);
        assert_eq!("UNG_VIBCH", record.db_id_code);
        assert_eq!(1, record.db_seq_begin);
        assert_eq!(None, record.i_dbns_beg);
        assert_eq!(226, record.db_seq_end);
        assert_eq!(None, record.db_ins_end);
        let line = "DBREF  2JHQ A    1   226  PDB    Q9KPK8   UNG_VIBCH        1A   226B";
        let record = DBRefRecord::new(line);
        assert_eq!("2JHQ", record.id_code);
        assert_eq!('A', record.chain_id);
        assert_eq!(1, record.seq_begin);
        assert_eq!(None, record.insert_begin);
        assert_eq!(226, record.seq_end);
        assert_eq!(None, record.insert_end);
        assert_eq!(DBType::PDB, record.database);
        assert_eq!("Q9KPK8", record.db_accession);
        assert_eq!("UNG_VIBCH", record.db_id_code);
        assert_eq!(1, record.db_seq_begin);
        assert_eq!(Some('A'), record.i_dbns_beg);
        assert_eq!(226, record.db_seq_end);
        assert_eq!(Some('B'), record.db_ins_end);
    }
}