1use std::fmt;
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
5pub enum Element {
6 H,
7 He,
8 Li,
9 Be,
10 B,
11 C,
12 N,
13 O,
14 F,
15 Ne,
16 Na,
17 Mg,
18 Al,
19 Si,
20 P,
21 S,
22 Cl,
23 Ar,
24 K,
25 Ca,
26 Ti,
27 V,
28 Cr,
29 Mn,
30 Fe,
31 Co,
32 Ni,
33 Cu,
34 Zn,
35 Ga,
36 Ge,
37 As,
38 Se,
39 Br,
40 Kr,
41 Rb,
42 Sr,
43 Zr,
44 Mo,
45 Ru,
46 Rh,
47 Pd,
48 Ag,
49 Cd,
50 In,
51 Sn,
52 Sb,
53 Te,
54 I,
55 Xe,
56 Cs,
57 Ba,
58 Pt,
59 Au,
60 Hg,
61 Tl,
62 Pb,
63 Bi,
64 Unknown,
65}
66
67impl Element {
68 pub fn from_symbol(s: &str) -> Option<Self> {
70 match s {
71 "H" => Some(Element::H),
72 "He" => Some(Element::He),
73 "Li" => Some(Element::Li),
74 "Be" => Some(Element::Be),
75 "B" => Some(Element::B),
76 "C" => Some(Element::C),
77 "N" => Some(Element::N),
78 "O" => Some(Element::O),
79 "F" => Some(Element::F),
80 "Ne" => Some(Element::Ne),
81 "Na" => Some(Element::Na),
82 "Mg" => Some(Element::Mg),
83 "Al" => Some(Element::Al),
84 "Si" => Some(Element::Si),
85 "P" => Some(Element::P),
86 "S" => Some(Element::S),
87 "Cl" => Some(Element::Cl),
88 "Ar" => Some(Element::Ar),
89 "K" => Some(Element::K),
90 "Ca" => Some(Element::Ca),
91 "Ti" => Some(Element::Ti),
92 "V" => Some(Element::V),
93 "Cr" => Some(Element::Cr),
94 "Mn" => Some(Element::Mn),
95 "Fe" => Some(Element::Fe),
96 "Co" => Some(Element::Co),
97 "Ni" => Some(Element::Ni),
98 "Cu" => Some(Element::Cu),
99 "Zn" => Some(Element::Zn),
100 "Ga" => Some(Element::Ga),
101 "Ge" => Some(Element::Ge),
102 "As" => Some(Element::As),
103 "Se" => Some(Element::Se),
104 "Br" => Some(Element::Br),
105 "Kr" => Some(Element::Kr),
106 "Rb" => Some(Element::Rb),
107 "Sr" => Some(Element::Sr),
108 "Zr" => Some(Element::Zr),
109 "Mo" => Some(Element::Mo),
110 "Ru" => Some(Element::Ru),
111 "Rh" => Some(Element::Rh),
112 "Pd" => Some(Element::Pd),
113 "Ag" => Some(Element::Ag),
114 "Cd" => Some(Element::Cd),
115 "In" => Some(Element::In),
116 "Sn" => Some(Element::Sn),
117 "Sb" => Some(Element::Sb),
118 "Te" => Some(Element::Te),
119 "I" => Some(Element::I),
120 "Xe" => Some(Element::Xe),
121 "Cs" => Some(Element::Cs),
122 "Ba" => Some(Element::Ba),
123 "Pt" => Some(Element::Pt),
124 "Au" => Some(Element::Au),
125 "Hg" => Some(Element::Hg),
126 "Tl" => Some(Element::Tl),
127 "Pb" => Some(Element::Pb),
128 "Bi" => Some(Element::Bi),
129 _ => None,
130 }
131 }
132
133 pub fn atomic_number(&self) -> u8 {
135 match self {
136 Element::H => 1,
137 Element::He => 2,
138 Element::Li => 3,
139 Element::Be => 4,
140 Element::B => 5,
141 Element::C => 6,
142 Element::N => 7,
143 Element::O => 8,
144 Element::F => 9,
145 Element::Ne => 10,
146 Element::Na => 11,
147 Element::Mg => 12,
148 Element::Al => 13,
149 Element::Si => 14,
150 Element::P => 15,
151 Element::S => 16,
152 Element::Cl => 17,
153 Element::Ar => 18,
154 Element::K => 19,
155 Element::Ca => 20,
156 Element::Ti => 22,
157 Element::V => 23,
158 Element::Cr => 24,
159 Element::Mn => 25,
160 Element::Fe => 26,
161 Element::Co => 27,
162 Element::Ni => 28,
163 Element::Cu => 29,
164 Element::Zn => 30,
165 Element::Ga => 31,
166 Element::Ge => 32,
167 Element::As => 33,
168 Element::Se => 34,
169 Element::Br => 35,
170 Element::Kr => 36,
171 Element::Rb => 37,
172 Element::Sr => 38,
173 Element::Zr => 40,
174 Element::Mo => 42,
175 Element::Ru => 44,
176 Element::Rh => 45,
177 Element::Pd => 46,
178 Element::Ag => 47,
179 Element::Cd => 48,
180 Element::In => 49,
181 Element::Sn => 50,
182 Element::Sb => 51,
183 Element::Te => 52,
184 Element::I => 53,
185 Element::Xe => 54,
186 Element::Cs => 55,
187 Element::Ba => 56,
188 Element::Pt => 78,
189 Element::Au => 79,
190 Element::Hg => 80,
191 Element::Tl => 81,
192 Element::Pb => 82,
193 Element::Bi => 83,
194 Element::Unknown => 0,
195 }
196 }
197
198 pub fn symbol(&self) -> &'static str {
200 match self {
201 Element::H => "H",
202 Element::He => "He",
203 Element::Li => "Li",
204 Element::Be => "Be",
205 Element::B => "B",
206 Element::C => "C",
207 Element::N => "N",
208 Element::O => "O",
209 Element::F => "F",
210 Element::Ne => "Ne",
211 Element::Na => "Na",
212 Element::Mg => "Mg",
213 Element::Al => "Al",
214 Element::Si => "Si",
215 Element::P => "P",
216 Element::S => "S",
217 Element::Cl => "Cl",
218 Element::Ar => "Ar",
219 Element::K => "K",
220 Element::Ca => "Ca",
221 Element::Ti => "Ti",
222 Element::V => "V",
223 Element::Cr => "Cr",
224 Element::Mn => "Mn",
225 Element::Fe => "Fe",
226 Element::Co => "Co",
227 Element::Ni => "Ni",
228 Element::Cu => "Cu",
229 Element::Zn => "Zn",
230 Element::Ga => "Ga",
231 Element::Ge => "Ge",
232 Element::As => "As",
233 Element::Se => "Se",
234 Element::Br => "Br",
235 Element::Kr => "Kr",
236 Element::Rb => "Rb",
237 Element::Sr => "Sr",
238 Element::Zr => "Zr",
239 Element::Mo => "Mo",
240 Element::Ru => "Ru",
241 Element::Rh => "Rh",
242 Element::Pd => "Pd",
243 Element::Ag => "Ag",
244 Element::Cd => "Cd",
245 Element::In => "In",
246 Element::Sn => "Sn",
247 Element::Sb => "Sb",
248 Element::Te => "Te",
249 Element::I => "I",
250 Element::Xe => "Xe",
251 Element::Cs => "Cs",
252 Element::Ba => "Ba",
253 Element::Pt => "Pt",
254 Element::Au => "Au",
255 Element::Hg => "Hg",
256 Element::Tl => "Tl",
257 Element::Pb => "Pb",
258 Element::Bi => "Bi",
259 Element::Unknown => "*",
260 }
261 }
262
263 pub fn default_valences(&self) -> &'static [u8] {
265 match self {
266 Element::B => &[3],
267 Element::C => &[4],
268 Element::N => &[3, 5],
269 Element::O => &[2],
270 Element::P => &[3, 5],
271 Element::S => &[2, 4, 6],
272 Element::F => &[1],
273 Element::Cl => &[1, 3, 5, 7],
274 Element::Br => &[1, 3, 5, 7],
275 Element::I => &[1, 3, 5, 7],
276 Element::Si => &[4],
277 Element::Al => &[3],
278 Element::Sn => &[2, 4],
279 Element::Pb => &[2, 4],
280 _ => &[0],
281 }
282 }
283
284 pub fn is_organic_subset(&self) -> bool {
286 matches!(
287 self,
288 Element::B
289 | Element::C
290 | Element::N
291 | Element::O
292 | Element::P
293 | Element::S
294 | Element::F
295 | Element::Cl
296 | Element::Br
297 | Element::I
298 )
299 }
300}
301
302impl fmt::Display for Element {
303 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304 write!(f, "{}", self.symbol())
305 }
306}
307
308#[derive(Clone, Debug)]
310pub struct Atom {
311 pub element: Element,
312 pub charge: i8,
313 pub isotope: Option<u16>,
314 pub h_count: u8,
316 pub explicit_h: Option<u8>,
319 pub aromatic: bool,
320 pub in_ring: bool,
321 pub ring_sizes: smallvec::SmallVec<[u8; 4]>,
322 pub map_num: Option<u16>,
323}
324
325impl Atom {
326 pub fn new(element: Element) -> Self {
328 Self {
329 element,
330 charge: 0,
331 isotope: None,
332 h_count: 0,
333 explicit_h: None,
334 aromatic: false,
335 in_ring: false,
336 ring_sizes: smallvec::SmallVec::new(),
337 map_num: None,
338 }
339 }
340}
341
342#[cfg(test)]
343mod tests {
344 use super::*;
345
346 #[test]
347 fn test_from_symbol() {
348 assert_eq!(Element::from_symbol("C"), Some(Element::C));
349 assert_eq!(Element::from_symbol("Cl"), Some(Element::Cl));
350 assert_eq!(Element::from_symbol("Br"), Some(Element::Br));
351 assert_eq!(Element::from_symbol("Xx"), None);
352 }
353
354 #[test]
355 fn test_default_valences() {
356 assert_eq!(Element::C.default_valences(), &[4]);
357 assert_eq!(Element::N.default_valences(), &[3, 5]);
358 assert_eq!(Element::S.default_valences(), &[2, 4, 6]);
359 }
360
361 #[test]
362 fn test_is_organic_subset() {
363 assert!(Element::C.is_organic_subset());
364 assert!(!Element::Fe.is_organic_subset());
365 }
366
367 #[test]
368 fn test_atomic_number() {
369 assert_eq!(Element::C.atomic_number(), 6);
370 assert_eq!(Element::N.atomic_number(), 7);
371 assert_eq!(Element::Fe.atomic_number(), 26);
372 }
373}