elements_rs 0.2.3

A comprehensive library for chemical elements and their isotopes with rich metadata
Documentation
//! Bonding capacity of chemical elements.

use crate::isotopes::ElementVariant;

/// Minimum and maximum number of bonds an element can form.
pub trait BondsNumber {
    /// Returns `(min_bonds, max_bonds)` for the element.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use elements_rs::{BondsNumber, Element};
    ///
    /// let (min, max) = Element::H.number_of_bonds();
    /// assert_eq!((min, max), (1, 1));
    ///
    /// let (min, max) = Element::C.number_of_bonds();
    /// assert_eq!((min, max), (4, 4));
    /// ```
    fn number_of_bonds(&self) -> (u8, u8);

    /// Returns `true` for noble gases (elements with zero bonds).
    ///
    /// # Examples
    ///
    /// ```rust
    /// use elements_rs::{BondsNumber, Element};
    ///
    /// assert!(Element::He.is_noble_gas());
    /// assert!(!Element::H.is_noble_gas());
    /// ```
    fn is_noble_gas(&self) -> bool {
        self.number_of_bonds() == (0, 0)
    }
}

impl BondsNumber for crate::Element {
    #[allow(clippy::match_same_arms)]
    #[allow(clippy::too_many_lines)]
    fn number_of_bonds(&self) -> (u8, u8) {
        match self {
            Self::H => (1, 1),
            Self::He => (0, 0),
            Self::Li => (1, 1),
            Self::Be => (2, 2),
            Self::B => (3, 3),
            Self::C => (4, 4),
            Self::N => (3, 3),
            Self::O => (2, 2),
            Self::F => (1, 1),
            Self::Ne => (0, 0),
            Self::Na => (1, 1),
            Self::Mg => (2, 2),
            Self::Al => (3, 3),
            Self::Si => (4, 4),
            Self::P => (3, 3),
            Self::S => (2, 2),
            Self::Cl => (1, 1),
            Self::Ar => (0, 0),
            Self::K => (1, 1),
            Self::Ca => (2, 2),
            Self::Sc => (3, 3),
            Self::Ti => (4, 4),
            Self::V => (5, 5),
            Self::Cr => (6, 6),
            Self::Mn => (7, 7),
            Self::Fe => (3, 3),
            Self::Co => (3, 3),
            Self::Ni => (2, 2),
            Self::Cu => (2, 2),
            Self::Zn => (2, 2),
            Self::Ga => (3, 3),
            Self::Ge => (4, 4),
            Self::As => (3, 3),
            Self::Se => (2, 2),
            Self::Br => (1, 1),
            Self::Kr => (0, 0),
            Self::Rb => (1, 1),
            Self::Sr => (2, 2),
            Self::Y => (3, 3),
            Self::Zr => (4, 4),
            Self::Nb => (5, 5),
            Self::Mo => (6, 6),
            Self::Tc => (7, 7),
            Self::Ru => (8, 8),
            Self::Rh => (6, 6),
            Self::Pd => (4, 4),
            Self::Ag => (1, 1),
            Self::Cd => (2, 2),
            Self::In => (3, 3),
            Self::Sn => (4, 4),
            Self::Sb => (3, 3),
            Self::Te => (2, 2),
            Self::I => (1, 1),
            Self::Xe => (0, 0),
            Self::Cs => (1, 1),
            Self::Ba => (2, 2),
            Self::La => (3, 6),
            Self::Ce => (1, 6),
            Self::Pr => (1, 6),
            Self::Nd => (1, 6),
            Self::Pm => (1, 6),
            Self::Sm => (1, 6),
            Self::Eu => (1, 6),
            Self::Gd => (1, 6),
            Self::Tb => (1, 6),
            Self::Dy => (1, 6),
            Self::Ho => (1, 6),
            Self::Er => (1, 6),
            Self::Tm => (1, 6),
            Self::Yb => (1, 6),
            Self::Lu => (1, 6),
            Self::Hf => (1, 3),
            Self::Ta => (1, 3),
            Self::W => (1, 3),
            Self::Re => (1, 3),
            Self::Os => (1, 3),
            Self::Ir => (1, 3),
            Self::Pt => (1, 3),
            Self::Au => (1, 3),
            Self::Hg => (1, 3),
            Self::Tl => (1, 3),
            Self::Pb => (1, 3),
            Self::Bi => (1, 3),
            Self::Po => (1, 3),
            Self::At => (1, 3),
            Self::Rn => (0, 0),
            Self::Fr => (1, 6),
            Self::Ra => (1, 6),
            Self::Ac => (1, 6),
            Self::Th => (1, 6),
            Self::Pa => (1, 6),
            Self::U => (1, 6),
            Self::Np => (1, 6),
            Self::Pu => (1, 6),
            Self::Am => (1, 3),
            Self::Cm => (1, 3),
            Self::Bk => (1, 3),
            Self::Cf => (1, 3),
            Self::Es => (1, 3),
            Self::Fm => (1, 3),
            Self::Md => (1, 3),
            Self::No => (1, 3),
            Self::Lr => (1, 3),
            Self::Rf => (1, 3),
            Self::Db => (1, 3),
            Self::Sg => (1, 3),
            Self::Bh => (1, 3),
            Self::Hs => (1, 3),
            Self::Mt => (1, 3),
            Self::Ds => (1, 3),
            Self::Rg => (1, 3),
            Self::Cn => (1, 3),
            Self::Nh => (1, 3),
            Self::Fl => (1, 3),
            Self::Mc => (1, 3),
            Self::Lv => (1, 3),
            Self::Ts => (1, 3),
            Self::Og => (0, 0),
        }
    }
}

impl BondsNumber for crate::Isotope {
    fn number_of_bonds(&self) -> (u8, u8) {
        self.element().number_of_bonds()
    }
}

#[cfg(test)]
mod tests {
    use strum::IntoEnumIterator;

    use super::BondsNumber;

    #[test]
    fn test_number_of_bonds() {
        for element in crate::Element::iter() {
            let (min, max) = element.number_of_bonds();
            assert!(min <= max, "Min bonds should be <= max bonds for {element:?}");
            assert!(max <= 8, "Max bonds should be <= 8 for {element:?}"); // Some elements have higher
        }
    }

    #[test]
    fn test_is_noble_gas() {
        for element in crate::Element::iter() {
            let _ = element.is_noble_gas(); // Just ensure it doesn't panic
        }
    }

    #[test]
    fn test_isotope_number_of_bonds() {
        for element in crate::Element::iter() {
            let (elem_min, elem_max) = element.number_of_bonds();
            let isotopes = element.isotopes();
            for isotope in isotopes {
                let (min, max) = isotope.number_of_bonds();
                assert_eq!(
                    min, elem_min,
                    "Min bonds should match for isotope {isotope:?} of {element:?}",
                );
                assert_eq!(
                    max, elem_max,
                    "Max bonds should match for isotope {isotope:?} of {element:?}",
                );
            }
        }
    }
}