1use mendeleev::{Element as MendeleevElement, ALL_ELEMENTS};
4
5use crate::{error::ElementError, parse};
6
7#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub struct SimpleElement {
11 pub name: String,
14 pub count: usize,
17}
18
19impl SimpleElement {
20 pub(crate) fn into_element(self) -> Result<Element, ElementError> {
21 Element::new(self)
22 }
23}
24
25#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize))]
31pub struct Element {
32 #[cfg_attr(feature = "serde", serde(skip))]
34 el: &'static MendeleevElement,
35 pub count: usize,
38}
39
40impl std::ops::Deref for Element {
41 type Target = MendeleevElement;
42
43 fn deref(&self) -> &'static Self::Target {
44 self.el
45 }
46}
47
48impl Element {
49 pub(crate) fn new(sim: SimpleElement) -> Result<Self, ElementError> {
51 let Some(elm) = ALL_ELEMENTS.iter().find(|n| n.symbol() == sim.name.as_str()) else {
53 return Err(ElementError::NotInPeriodicTable(sim.name));
54 };
55
56 Ok(Self {
57 el: elm,
58 count: sim.count,
59 })
60 }
61
62 pub fn parse(input: &str) -> Result<Self, ElementError> {
76 match parse::parse_element(input) {
77 Ok((i, eq)) if i.trim().is_empty() => Ok(eq),
78 Ok((i, _)) => Err(ElementError::TooMuchInput(i.to_string())),
79 Err(nom::Err::Error(e) | nom::Err::Failure(e)) => {
80 Err(ElementError::ParseError(e.into()))
81 }
82 Err(nom::Err::Incomplete(_)) => unreachable!(),
84 }
85 }
86}
87
88impl TryFrom<SimpleElement> for Element {
89 type Error = ElementError;
90
91 fn try_from(s: SimpleElement) -> Result<Self, Self::Error> {
92 Self::new(s)
93 }
94}
95
96impl Default for Element {
97 fn default() -> Self {
98 Self {
99 el: &MendeleevElement::H,
100 count: 1,
101 }
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn construct_element() {
111 let simple = SimpleElement {
112 name: "Pb".to_string(),
113 count: 2,
114 };
115 assert!(simple.into_element().is_ok());
116 }
117
118 #[test]
119 fn invalid_element() {
120 let simple = SimpleElement {
121 name: "Bill".to_string(),
122 count: 0xCAFE,
123 };
124 assert_eq!(
125 simple.into_element(),
126 Err(ElementError::NotInPeriodicTable("Bill".to_string()))
127 )
128 }
129}