protein_core/structure/
atom.rs

1use super::AtomSerial;
2use super::{Element, Residue};
3use serde::Serialize;
4use std::str::FromStr;
5
6/// Represents an atom in a structural model.
7///
8/// # Field
9///
10/// - `id`: Unique identifier. No two atoms. No two `Atom`s in the same `Model` can have the same `id`.
11/// - `name`: Atom name. This represents the exact "role" of an atom in a given residue.
12/// - `residue`: The [`Residue`] this atom belongs to. This can be an amino acid, a nucleotide, water, or a custom molecule.
13/// - `chain`: The chain identifier
14/// - `sequence_number`: The position of this atom in the chain
15/// - `coord`: Cartesian coordinates `x, y, z`
16/// - `occupancy`:
17/// - `temperature_factor`:
18/// - `element`: The [`Element`]
19/// - `chaege`: The charge on the atom
20///
21/// [`Residue`]: ../residue/struct.Residue.html
22/// [`Element`]: ../element/enum.Element.html
23#[derive(Debug, Clone, Serialize)]
24pub struct Atom {
25    pub id: AtomSerial,
26    pub name: AtomName,
27    pub id1: char,
28    pub residue: Residue,
29    pub chain: char,
30    pub sequence_number: u32,
31    pub insertion_code: char,
32    pub coord: [f32; 3],
33    pub occupancy: f32,
34    pub temperature_factor: f32,
35    pub element: Element,
36    pub charge: i8,
37    // pub is_hetatom: bool, // ! implied
38}
39
40pub struct GenericAtomParser;
41
42/// Internally, an `AtomName` is represented as an array of 4 bytes.
43///
44/// For convinience, `FromStr` is implemented for `AtomName`, so you can:
45///
46/// ```
47/// use std::str::FromStr;
48/// use protein_core::structure::AtomName;
49///
50/// let atom = AtomName::from_str("O2").unwrap();
51/// assert_eq!(atom, AtomName([b'O', b'2', b' ', b' ']));
52/// ```
53#[derive(Debug, Clone, Serialize, Eq, PartialEq)]
54pub struct AtomName(pub [u8; 4]);
55
56impl AtomName {
57    pub(crate) fn is_n(&self) -> bool {
58        &self.0 == b"N   "
59    }
60    pub(crate) fn is_c(&self) -> bool {
61        &self.0 == b"C   "
62    }
63    pub(crate) fn is_ca(&self) -> bool {
64        &self.0 == b"CA  "
65    }
66    pub(crate) fn is_o(&self) -> bool {
67        &self.0 == b"O   "
68    }
69}
70
71#[derive(Debug)]
72pub enum ParseAtomNameError {
73    LengthGreaterThan4,
74    LengthZero,
75}
76
77impl FromStr for AtomName {
78    type Err = ParseAtomNameError;
79    fn from_str(s: &str) -> Result<Self, ParseAtomNameError> {
80        let s = s.as_bytes();
81        match s.len() {
82            1usize => Ok(AtomName([s[0], b' ', b' ', b' '])),
83            2usize => Ok(AtomName([s[0], s[1], b' ', b' '])),
84            3usize => Ok(AtomName([s[0], s[1], s[2], b' '])),
85            4usize => Ok(AtomName([s[0], s[1], s[2], s[3]])),
86            0usize => Err(ParseAtomNameError::LengthZero),
87            _ => Err(ParseAtomNameError::LengthGreaterThan4),
88        }
89    }
90}
91
92// #[derive(Debug, Clone, Serialize)]
93// pub enum AtomName {
94//     AminoAcid(AminoAcidAtomName),
95//     Nucleotide(NucleotideAtomName),
96//     WaterO,
97//     Other(String),
98// }
99
100// #[derive(Debug, Clone, Serialize)]
101// pub enum AminoAcidAtomName {
102//     N,
103//     CA,
104//     C,
105//     O,
106//     Other(String),
107// }
108
109// impl AminoAcidAtomName {
110//     pub fn from_bytes(inp: &[u8]) -> Self {
111//         match inp {
112//             b"N" => Self::N,
113//             b"CA" => Self::CA,
114//             b"C" => Self::C,
115//             b"O" => Self::O,
116//             _ => Self::Other(unsafe { std::str::from_utf8_unchecked(inp).to_owned() }),
117//         }
118//     }
119//     pub fn from_bytes_fixed4(inp: &[u8]) -> Self {
120//         match inp {
121//             b" N  " => Self::N,
122//             b" CA " => Self::CA,
123//             b" C  " => Self::C,
124//             b" O  " => Self::O,
125//             _ => Self::Other(unsafe { std::str::from_utf8_unchecked(inp).trim_start().to_owned() }),
126//         }
127//     }
128// }
129
130// #[derive(Debug, Clone, Serialize)]
131// pub enum NucleotideAtomName {
132//     OP1,
133//     OP2,
134//     O5,
135//     O4,
136//     O3,
137//     O2,
138//     C5,
139//     C4,
140//     C3,
141//     C2,
142//     C1,
143//     N9,
144//     N7,
145//     N6,
146//     N4,
147//     N3,
148//     N2,
149//     N1,
150//     P,
151//     Other(String),
152// }
153
154// impl NucleotideAtomName {
155//     pub fn from_bytes_uppercase(inp: &[u8]) -> Self {
156//         match inp {
157//             b"OP1" => Self::OP1,
158//             b"OP2" => Self::OP2,
159//             b"O5" => Self::O5,
160//             b"O4" => Self::O4,
161//             b"O3" => Self::O3,
162//             b"O2" => Self::O2,
163//             b"C5" => Self::C5,
164//             b"C4" => Self::C4,
165//             b"C3" => Self::C3,
166//             b"C2" => Self::C2,
167//             b"C1" => Self::C1,
168//             b"N9" => Self::N9,
169//             b"N7" => Self::N7,
170//             b"N6" => Self::N6,
171//             b"N4" => Self::N4,
172//             b"N3" => Self::N3,
173//             b"N2" => Self::N2,
174//             b"N1" => Self::N1,
175//             b"P" => Self::P,
176//             _ => Self::Other(unsafe { std::str::from_utf8_unchecked(inp).to_owned() }),
177//         }
178//     }
179//     pub fn from_bytes_uppercase_fixed4(inp: &[u8]) -> Self {
180//         match inp {
181//             b" OP1" => Self::OP1,
182//             b" OP2" => Self::OP2,
183//             b" O5 " => Self::O5,
184//             b" O4 " => Self::O4,
185//             b" O3 " => Self::O3,
186//             b" O2 " => Self::O2,
187//             b" C5 " => Self::C5,
188//             b" C4 " => Self::C4,
189//             b" C3 " => Self::C3,
190//             b" C2 " => Self::C2,
191//             b" C1 " => Self::C1,
192//             b" N9 " => Self::N9,
193//             b" N7 " => Self::N7,
194//             b" N6 " => Self::N6,
195//             b" N4 " => Self::N4,
196//             b" N3 " => Self::N3,
197//             b" N2 " => Self::N2,
198//             b" N1 " => Self::N1,
199//             b" P  " => Self::P,
200//             _ => Self::Other(unsafe { std::str::from_utf8_unchecked(inp).trim_start().to_owned() }),
201//         }
202//     }
203// }