use mendeleev::{Element as MendeleevElement, ALL_ELEMENTS};
use crate::{error::ElementError, parse};
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SimpleElement {
pub name: String,
pub count: usize,
}
impl SimpleElement {
pub(crate) fn into_element(self) -> Result<Element, ElementError> {
Element::new(self)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Element {
#[cfg_attr(feature = "serde", serde(skip))]
el: &'static MendeleevElement,
pub count: usize,
}
impl std::ops::Deref for Element {
type Target = MendeleevElement;
fn deref(&self) -> &'static Self::Target {
self.el
}
}
impl Element {
pub(crate) fn new(sim: SimpleElement) -> Result<Self, ElementError> {
let Some(elm) = ALL_ELEMENTS.iter().find(|n| n.symbol() == sim.name.as_str()) else {
return Err(ElementError::NotInPeriodicTable(sim.name));
};
Ok(Self {
el: elm,
count: sim.count,
})
}
pub fn parse(input: &str) -> Result<Self, ElementError> {
match parse::parse_element(input) {
Ok((i, eq)) if i.trim().is_empty() => Ok(eq),
Ok((i, _)) => Err(ElementError::TooMuchInput(i.to_string())),
Err(nom::Err::Error(e) | nom::Err::Failure(e)) => {
Err(ElementError::ParseError(e.into()))
}
Err(nom::Err::Incomplete(_)) => unreachable!(),
}
}
}
impl TryFrom<SimpleElement> for Element {
type Error = ElementError;
fn try_from(s: SimpleElement) -> Result<Self, Self::Error> {
Self::new(s)
}
}
impl Default for Element {
fn default() -> Self {
Self {
el: &MendeleevElement::H,
count: 1,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn construct_element() {
let simple = SimpleElement {
name: "Pb".to_string(),
count: 2,
};
assert!(simple.into_element().is_ok());
}
#[test]
fn invalid_element() {
let simple = SimpleElement {
name: "Bill".to_string(),
count: 0xCAFE,
};
assert_eq!(
simple.into_element(),
Err(ElementError::NotInPeriodicTable("Bill".to_string()))
)
}
}