Skip to main content

use_oxidation_state/
oxidation_state_set.rs

1use std::fmt;
2use std::slice;
3
4use crate::OxidationStateAssignment;
5
6/// A small insertion-order collection of oxidation-state assignments.
7#[derive(Clone, Debug, Default, Eq, PartialEq)]
8pub struct OxidationStateSet {
9    assignments: Vec<OxidationStateAssignment>,
10}
11
12impl OxidationStateSet {
13    /// Creates an empty oxidation-state set.
14    #[must_use]
15    pub const fn new() -> Self {
16        Self {
17            assignments: Vec::new(),
18        }
19    }
20
21    /// Creates a set from assignments, replacing earlier entries with the same label.
22    #[must_use]
23    pub fn from_assignments(
24        assignments: impl IntoIterator<Item = OxidationStateAssignment>,
25    ) -> Self {
26        let mut set = Self::new();
27
28        for assignment in assignments {
29            set.insert(assignment);
30        }
31
32        set
33    }
34
35    /// Inserts an assignment and returns the previous assignment with the same label, if any.
36    pub fn insert(
37        &mut self,
38        assignment: OxidationStateAssignment,
39    ) -> Option<OxidationStateAssignment> {
40        if let Some(existing) = self
41            .assignments
42            .iter_mut()
43            .find(|existing| existing.label() == assignment.label())
44        {
45            return Some(std::mem::replace(existing, assignment));
46        }
47
48        self.assignments.push(assignment);
49        None
50    }
51
52    /// Returns an assignment by label.
53    #[must_use]
54    pub fn get(&self, label: &str) -> Option<&OxidationStateAssignment> {
55        let label = label.trim();
56        self.assignments
57            .iter()
58            .find(|assignment| assignment.label() == label)
59    }
60
61    /// Returns `true` when an assignment for `label` exists.
62    #[must_use]
63    pub fn contains_label(&self, label: &str) -> bool {
64        self.get(label).is_some()
65    }
66
67    /// Returns the number of assignments.
68    #[must_use]
69    pub fn len(&self) -> usize {
70        self.assignments.len()
71    }
72
73    /// Returns `true` when there are no assignments.
74    #[must_use]
75    pub fn is_empty(&self) -> bool {
76        self.assignments.is_empty()
77    }
78
79    /// Returns an iterator over assignments.
80    pub fn iter(&self) -> slice::Iter<'_, OxidationStateAssignment> {
81        self.assignments.iter()
82    }
83}
84
85impl fmt::Display for OxidationStateSet {
86    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
87        let mut assignments = self.assignments.iter();
88
89        let Some(first) = assignments.next() else {
90            return Ok(());
91        };
92
93        write!(formatter, "{first}")?;
94
95        for assignment in assignments {
96            write!(formatter, ", {assignment}")?;
97        }
98
99        Ok(())
100    }
101}
102
103impl IntoIterator for OxidationStateSet {
104    type IntoIter = std::vec::IntoIter<Self::Item>;
105    type Item = OxidationStateAssignment;
106
107    fn into_iter(self) -> Self::IntoIter {
108        self.assignments.into_iter()
109    }
110}
111
112impl<'a> IntoIterator for &'a OxidationStateSet {
113    type IntoIter = slice::Iter<'a, OxidationStateAssignment>;
114    type Item = &'a OxidationStateAssignment;
115
116    fn into_iter(self) -> Self::IntoIter {
117        self.iter()
118    }
119}