hoomd_utility/valid/
positive_real.rs

1// Copyright (c) 2024-2026 The Regents of the University of Michigan.
2// Part of hoomd-rs, released under the BSD 3-Clause License.
3
4//! Implement `PositiveReal`
5
6use serde::{Deserialize, Serialize};
7use std::{
8    fmt,
9    ops::{Div, DivAssign, Mul, MulAssign},
10};
11
12use super::Error;
13
14/// A f64 value that is not +/- inf, nan, or a value <= 0.
15///
16/// # Example
17///
18/// ```
19/// use hoomd_utility::valid::PositiveReal;
20///
21/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
22/// let positive = PositiveReal::try_from(1.0)?;
23/// # Ok(())
24/// # }
25/// ```
26#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
27pub struct PositiveReal(f64);
28
29impl PositiveReal {
30    /// Access the value.
31    ///
32    /// # Example
33    ///
34    /// ```
35    /// use hoomd_utility::valid::PositiveReal;
36    ///
37    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
38    /// let positive = PositiveReal::try_from(1.0)?;
39    ///
40    /// assert_eq!(positive.get(), 1.0);
41    /// # Ok(())
42    /// # }
43    #[must_use]
44    #[inline]
45    pub fn get(&self) -> f64 {
46        self.0
47    }
48}
49
50impl TryFrom<f64> for PositiveReal {
51    type Error = Error;
52
53    /// Convert [`f64`] to [`PositiveReal`].
54    ///
55    /// # Example
56    ///
57    /// Valid conversion:
58    /// ```
59    /// use hoomd_utility::valid::PositiveReal;
60    ///
61    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
62    /// let positive = PositiveReal::try_from(1.0)?;
63    /// # Ok(())
64    /// # }
65    /// ```
66    ///
67    /// Invalid conversion
68    /// ```
69    /// use hoomd_utility::valid::PositiveReal;
70    ///
71    /// let result = PositiveReal::try_from(-1.0);
72    /// assert!(matches!(
73    ///     result,
74    ///     Err(hoomd_utility::valid::Error::NotPositive(_))
75    /// ));
76    /// ```
77    ///
78    /// # Errors
79    ///
80    /// [`Error::NotFinite`] when `v` is not finite.
81    /// [`Error::NotPositive`] when `v` is not a positive value
82    #[inline]
83    fn try_from(v: f64) -> Result<PositiveReal, Error> {
84        if !v.is_finite() {
85            Err(Error::NotFinite(v))
86        } else if v <= 0.0 {
87            Err(Error::NotPositive(v))
88        } else {
89            Ok(PositiveReal(v))
90        }
91    }
92}
93
94impl Default for PositiveReal {
95    /// The default value is 1.0.
96    #[inline]
97    fn default() -> Self {
98        PositiveReal(1.0)
99    }
100}
101
102impl fmt::Display for PositiveReal {
103    #[inline]
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        self.0.fmt(f)
106    }
107}
108
109impl Mul for PositiveReal {
110    type Output = Self;
111
112    #[inline]
113    fn mul(self, rhs: Self) -> Self {
114        Self(self.0 * rhs.0)
115    }
116}
117
118impl MulAssign<PositiveReal> for PositiveReal {
119    #[inline]
120    fn mul_assign(&mut self, rhs: PositiveReal) {
121        self.0 *= rhs.0;
122    }
123}
124
125impl Div for PositiveReal {
126    type Output = Self;
127
128    #[inline]
129    fn div(self, rhs: Self) -> Self {
130        Self(self.0 / rhs.0)
131    }
132}
133
134impl DivAssign<PositiveReal> for PositiveReal {
135    #[inline]
136    fn div_assign(&mut self, rhs: PositiveReal) {
137        self.0 /= rhs.0;
138    }
139}
140
141#[cfg(test)]
142mod tests {
143    use super::*;
144    use assert2::check;
145
146    #[test]
147    fn positive_real_validation() {
148        let result = PositiveReal::try_from(f64::INFINITY);
149        check!(result == Err(Error::NotFinite(f64::INFINITY)));
150
151        let result = PositiveReal::try_from(-f64::INFINITY);
152        check!(result == Err(Error::NotFinite(-f64::INFINITY)));
153
154        let result = PositiveReal::try_from(f64::NAN);
155        check!(matches!(result, Err(Error::NotFinite(_))));
156
157        let result = PositiveReal::try_from(0.0);
158        check!(result == Err(Error::NotPositive(0.0)));
159
160        let result = PositiveReal::try_from(-1.0);
161        check!(result == Err(Error::NotPositive(-1.0)));
162    }
163}