Skip to main content

use_stoichiometry/
yield.rs

1use std::fmt;
2
3use crate::StoichiometryValidationError;
4
5/// A positive finite theoretical yield value.
6#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
7pub struct TheoreticalYield(f64);
8
9impl TheoreticalYield {
10    /// Creates a theoretical yield value.
11    ///
12    /// # Errors
13    ///
14    /// Returns [`StoichiometryValidationError::NonFiniteYield`] when `value` is not
15    /// finite, or [`StoichiometryValidationError::NonPositiveTheoreticalYield`] when it
16    /// is zero or negative.
17    pub fn new(value: f64) -> Result<Self, StoichiometryValidationError> {
18        validate_finite_yield(value)?;
19
20        if value <= 0.0 {
21            Err(StoichiometryValidationError::NonPositiveTheoreticalYield)
22        } else {
23            Ok(Self(value))
24        }
25    }
26
27    /// Returns the yield value.
28    #[must_use]
29    pub const fn value(self) -> f64 {
30        self.0
31    }
32}
33
34impl fmt::Display for TheoreticalYield {
35    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
36        write!(formatter, "{}", self.0)
37    }
38}
39
40/// A nonnegative finite actual yield value.
41#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
42pub struct ActualYield(f64);
43
44impl ActualYield {
45    /// Creates an actual yield value.
46    ///
47    /// # Errors
48    ///
49    /// Returns [`StoichiometryValidationError::NonFiniteYield`] when `value` is not
50    /// finite, or [`StoichiometryValidationError::NegativeYield`] when it is negative.
51    pub fn new(value: f64) -> Result<Self, StoichiometryValidationError> {
52        validate_nonnegative_yield(value).map(Self)
53    }
54
55    /// Returns the yield value.
56    #[must_use]
57    pub const fn value(self) -> f64 {
58        self.0
59    }
60}
61
62impl fmt::Display for ActualYield {
63    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
64        write!(formatter, "{}", self.0)
65    }
66}
67
68/// A nonnegative finite percent yield value.
69#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
70pub struct PercentYield(f64);
71
72impl PercentYield {
73    /// Creates a percent yield value.
74    ///
75    /// # Errors
76    ///
77    /// Returns [`StoichiometryValidationError::NonFiniteYield`] when `value` is not
78    /// finite, or [`StoichiometryValidationError::NegativeYield`] when it is negative.
79    pub fn new(value: f64) -> Result<Self, StoichiometryValidationError> {
80        validate_nonnegative_yield(value).map(Self)
81    }
82
83    /// Calculates percent yield from actual and theoretical yield values.
84    ///
85    /// # Errors
86    ///
87    /// Returns a validation error when either yield value is invalid, or when the
88    /// theoretical yield is zero or negative.
89    pub fn from_actual_and_theoretical(
90        actual: f64,
91        theoretical: f64,
92    ) -> Result<Self, StoichiometryValidationError> {
93        Self::from_yields(
94            ActualYield::new(actual)?,
95            TheoreticalYield::new(theoretical)?,
96        )
97    }
98
99    /// Calculates percent yield from validated yield wrappers.
100    ///
101    /// # Errors
102    ///
103    /// Returns [`StoichiometryValidationError::NonFiniteYield`] if the calculated percent
104    /// yield is not finite.
105    pub fn from_yields(
106        actual: ActualYield,
107        theoretical: TheoreticalYield,
108    ) -> Result<Self, StoichiometryValidationError> {
109        Self::new((actual.value() / theoretical.value()) * 100.0)
110    }
111
112    /// Returns the percent yield value.
113    #[must_use]
114    pub const fn value(self) -> f64 {
115        self.0
116    }
117}
118
119impl fmt::Display for PercentYield {
120    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
121        write!(formatter, "{}%", self.0)
122    }
123}
124
125fn validate_finite_yield(value: f64) -> Result<(), StoichiometryValidationError> {
126    if value.is_finite() {
127        Ok(())
128    } else {
129        Err(StoichiometryValidationError::NonFiniteYield)
130    }
131}
132
133fn validate_nonnegative_yield(value: f64) -> Result<f64, StoichiometryValidationError> {
134    validate_finite_yield(value)?;
135
136    if value < 0.0 {
137        Err(StoichiometryValidationError::NegativeYield)
138    } else {
139        Ok(value)
140    }
141}