Skip to main content

use_ion/
monatomic_ion.rs

1use std::fmt;
2
3use use_chemical_formula::ChemicalFormula;
4
5use crate::{Ion, IonCharge, IonKind, IonValidationError};
6
7/// A monatomic ion wrapper.
8#[derive(Clone, Debug, Eq, PartialEq)]
9pub struct MonatomicIon(Ion);
10
11impl MonatomicIon {
12    /// Creates a monatomic ion from a formula and charge.
13    ///
14    /// # Errors
15    ///
16    /// Returns [`IonValidationError::ExpectedMonatomicFormula`] when `formula` does not contain
17    /// exactly one atom.
18    pub fn new(formula: ChemicalFormula, charge: IonCharge) -> Result<Self, IonValidationError> {
19        Self::from_ion(Ion::new(formula, charge))
20    }
21
22    /// Wraps an existing ion after monatomic formula validation.
23    ///
24    /// # Errors
25    ///
26    /// Returns [`IonValidationError::ExpectedMonatomicFormula`] when the ion formula does not
27    /// contain exactly one atom.
28    pub fn from_ion(ion: Ion) -> Result<Self, IonValidationError> {
29        if is_monatomic(ion.formula()) {
30            Ok(Self(ion.with_kind(IonKind::Monatomic)))
31        } else {
32            Err(IonValidationError::ExpectedMonatomicFormula)
33        }
34    }
35
36    /// Returns the wrapped ion.
37    #[must_use]
38    pub const fn as_ion(&self) -> &Ion {
39        &self.0
40    }
41
42    /// Consumes the wrapper and returns the ion.
43    #[must_use]
44    pub fn into_ion(self) -> Ion {
45        self.0
46    }
47}
48
49impl fmt::Display for MonatomicIon {
50    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
51        write!(formatter, "{}", self.0)
52    }
53}
54
55fn is_monatomic(formula: &ChemicalFormula) -> bool {
56    let counts = formula.element_counts();
57    counts.len() == 1 && counts.values().copied().sum::<u64>() == 1
58}