use std::fmt;
use std::ops::Range;
use crate::ffi::bridge as sys;
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Element {
first: u32,
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[non_exhaustive]
pub struct Isotope {
pub mass: f64,
pub abundance: f64,
pub mass_number: u32,
}
impl Element {
pub fn from_symbol(symbol: &str) -> Option<Element> {
let first = sys::element_find(symbol);
Element::from_first_index(first)
}
pub(crate) fn from_first_index(first: i32) -> Option<Element> {
if first < 0 || sys::element_isotope_count(first) == 0 {
return None;
}
Some(Element {
first: first as u32,
})
}
pub(crate) fn first_index(self) -> i32 {
self.first as i32
}
fn rows(self) -> Range<usize> {
let first = self.first as usize;
first..first + sys::element_isotope_count(self.first as i32) as usize
}
pub fn all() -> impl Iterator<Item = Element> {
let len = sys::element_table_len();
let mut row = 0usize;
std::iter::from_fn(move || {
if row >= len {
return None;
}
let element = Element { first: row as u32 };
row += sys::element_isotope_count(row as i32).max(1) as usize;
Some(element)
})
}
pub fn symbol(self) -> &'static str {
sys::element_table_symbol(self.first as usize)
}
pub fn name(self) -> &'static str {
sys::element_table_name(self.first as usize)
}
pub fn atomic_number(self) -> u32 {
sys::element_table_atomic_number(self.first as usize) as u32
}
pub fn isotopes(self) -> impl ExactSizeIterator<Item = Isotope> {
self.rows().map(|row| Isotope {
mass: sys::element_table_mass(row),
abundance: sys::element_table_probability(row),
mass_number: sys::element_table_mass_number(row) as u32,
})
}
}
impl fmt::Debug for Element {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Element({})", self.symbol())
}
}
impl fmt::Display for Element {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.symbol())
}
}