Skip to main content

use_oxidation_state/
oxidation_state.rs

1use std::fmt;
2
3use crate::{
4    OxidationMagnitude, OxidationSign, OxidationStateValidationError, RomanOxidationState,
5};
6
7/// A validated oxidation-state value.
8#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
9pub struct OxidationState {
10    sign: OxidationSign,
11    magnitude: OxidationMagnitude,
12}
13
14impl OxidationState {
15    /// Creates an oxidation state from a sign and magnitude.
16    ///
17    /// # Errors
18    ///
19    /// Returns [`OxidationStateValidationError::ZeroSignedMagnitude`] when a positive or
20    /// negative state uses zero magnitude, or
21    /// [`OxidationStateValidationError::NonZeroZeroMagnitude`] when a zero state uses a
22    /// nonzero magnitude.
23    pub const fn new(
24        sign: OxidationSign,
25        magnitude: OxidationMagnitude,
26    ) -> Result<Self, OxidationStateValidationError> {
27        match (sign, magnitude.get()) {
28            (OxidationSign::Positive | OxidationSign::Negative, 0) => {
29                Err(OxidationStateValidationError::ZeroSignedMagnitude)
30            },
31            (OxidationSign::Zero, 0) => Ok(Self { sign, magnitude }),
32            (OxidationSign::Zero, _) => Err(OxidationStateValidationError::NonZeroZeroMagnitude),
33            _ => Ok(Self { sign, magnitude }),
34        }
35    }
36
37    /// Creates a positive oxidation state.
38    ///
39    /// # Errors
40    ///
41    /// Returns [`OxidationStateValidationError::ZeroSignedMagnitude`] when `magnitude` is
42    /// zero, or [`OxidationStateValidationError::MagnitudeAboveMaximum`] when it is above
43    /// the supported range.
44    pub fn positive(magnitude: u8) -> Result<Self, OxidationStateValidationError> {
45        let magnitude = OxidationMagnitude::new(magnitude)?;
46
47        Self::new(OxidationSign::Positive, magnitude)
48    }
49
50    /// Creates a negative oxidation state.
51    ///
52    /// # Errors
53    ///
54    /// Returns [`OxidationStateValidationError::ZeroSignedMagnitude`] when `magnitude` is
55    /// zero, or [`OxidationStateValidationError::MagnitudeAboveMaximum`] when it is above
56    /// the supported range.
57    pub fn negative(magnitude: u8) -> Result<Self, OxidationStateValidationError> {
58        let magnitude = OxidationMagnitude::new(magnitude)?;
59
60        Self::new(OxidationSign::Negative, magnitude)
61    }
62
63    /// Creates a zero oxidation state.
64    #[must_use]
65    pub const fn zero() -> Self {
66        Self {
67            sign: OxidationSign::Zero,
68            magnitude: OxidationMagnitude::ZERO,
69        }
70    }
71
72    /// Returns the oxidation-state sign.
73    #[must_use]
74    pub const fn sign(self) -> OxidationSign {
75        self.sign
76    }
77
78    /// Returns the oxidation-state magnitude.
79    #[must_use]
80    pub const fn magnitude(self) -> OxidationMagnitude {
81        self.magnitude
82    }
83
84    /// Returns the oxidation-state magnitude value.
85    #[must_use]
86    pub const fn magnitude_value(self) -> u8 {
87        self.magnitude.get()
88    }
89
90    /// Returns the signed oxidation-state value.
91    #[must_use]
92    pub const fn signed_value(self) -> i8 {
93        match self.sign {
94            OxidationSign::Positive => self.magnitude.get() as i8,
95            OxidationSign::Negative => -(self.magnitude.get() as i8),
96            OxidationSign::Zero => 0,
97        }
98    }
99
100    /// Returns `true` for positive oxidation states.
101    #[must_use]
102    pub const fn is_positive(self) -> bool {
103        self.sign.is_positive()
104    }
105
106    /// Returns `true` for negative oxidation states.
107    #[must_use]
108    pub const fn is_negative(self) -> bool {
109        self.sign.is_negative()
110    }
111
112    /// Returns `true` for zero oxidation states.
113    #[must_use]
114    pub const fn is_zero(self) -> bool {
115        self.sign.is_zero()
116    }
117
118    /// Returns the Roman numeral for the nonzero magnitude.
119    #[must_use]
120    pub fn to_roman(self) -> Option<String> {
121        RomanOxidationState::from_state(self).map(|roman| roman.to_string())
122    }
123}
124
125impl Default for OxidationState {
126    fn default() -> Self {
127        Self::zero()
128    }
129}
130
131impl fmt::Display for OxidationState {
132    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
133        match self.sign {
134            OxidationSign::Positive => write!(formatter, "+{}", self.magnitude),
135            OxidationSign::Negative => write!(formatter, "-{}", self.magnitude),
136            OxidationSign::Zero => formatter.write_str("0"),
137        }
138    }
139}