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