use crate::tokens::{Element, Substance};
use crate::Component;
use std::collections::HashMap;
#[derive(Clone, Debug, PartialEq)]
pub struct Compound {
composition: Vec<Element>,
components: HashMap<&'static str, Component>,
molar_mass: f32,
}
impl Compound {
pub(crate) fn new() -> Self {
Self {
composition: Vec::new(),
components: HashMap::new(),
molar_mass: 0.0,
}
}
pub fn composition(&self) -> &Vec<Element> {
&self.composition
}
pub fn components(&self) -> &HashMap<&'static str, Component> {
&self.components
}
pub fn molar_mass(&self) -> f32 {
self.molar_mass
}
fn add_element(&mut self, element: Element) {
self.components
.entry(&element.chemical_element().symbol())
.and_modify(|component| component.add_atoms(element.subscript() as usize))
.or_insert(Component::from(element));
self.composition.push(element);
self.molar_mass += element.chemical_element().atomic_weight() * element.subscript() as f32;
}
fn calculate_mass_percentage(&mut self) {
self.components
.values_mut()
.for_each(|component| component.calculate_mass_percent(self.molar_mass));
}
}
impl From<Substance> for Compound {
fn from(substance: Substance) -> Self {
let mut compound = Self::new();
substance.elements().iter().for_each(|element| {
compound.add_element(*element);
});
compound.calculate_mass_percentage();
compound
}
}
#[cfg(test)]
mod tests {
use super::Compound;
use crate::tokens::{Component, Element, Hydrate, Substance};
#[test]
fn molar_mass_calculation() {
const MAGNESIUM_SULFATE_MOLAR_MASS: f32 = 246.466;
let compound = Compound::from(Substance::from(
1,
vec![
Component::Element(Element::from("Mg", 1)),
Component::Element(Element::from("S", 1)),
Component::Element(Element::from("O", 4)),
],
Some(Hydrate::from(
7,
vec![Element::from("H", 2), Element::from("O", 1)],
)),
));
assert_eq!(compound.molar_mass(), MAGNESIUM_SULFATE_MOLAR_MASS);
}
}