hoomd_utility/valid/
open_unit_interval_number.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 `OpenUnitIntervalNumber`
5
6use serde::{Deserialize, Serialize};
7use std::fmt;
8
9use super::Error;
10
11/// A f64 value in the interval (0,1)
12///
13/// # Example
14///
15/// ```
16/// use hoomd_utility::valid::OpenUnitIntervalNumber;
17///
18/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
19/// let v = OpenUnitIntervalNumber::try_from(0.5)?;
20/// # Ok(())
21/// # }
22/// ```
23#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
24pub struct OpenUnitIntervalNumber(f64);
25
26impl OpenUnitIntervalNumber {
27    /// Access the value.
28    ///
29    /// # Example
30    ///
31    /// ```
32    /// use hoomd_utility::valid::OpenUnitIntervalNumber;
33    ///
34    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
35    /// let v = OpenUnitIntervalNumber::try_from(0.5)?;
36    ///
37    /// assert_eq!(v.get(), 0.5);
38    /// # Ok(())
39    /// # }
40    #[must_use]
41    #[inline]
42    pub fn get(&self) -> f64 {
43        self.0
44    }
45}
46
47impl TryFrom<f64> for OpenUnitIntervalNumber {
48    type Error = Error;
49
50    /// Convert [`f64`] to [`OpenUnitIntervalNumber`].
51    ///
52    /// # Example
53    ///
54    /// Valid conversion:
55    /// ```
56    /// use hoomd_utility::valid::OpenUnitIntervalNumber;
57    ///
58    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
59    /// let v = OpenUnitIntervalNumber::try_from(0.5)?;
60    /// # Ok(())
61    /// # }
62    /// ```
63    ///
64    /// Invalid conversion
65    /// ```
66    /// use hoomd_utility::valid::OpenUnitIntervalNumber;
67    ///
68    /// let result = OpenUnitIntervalNumber::try_from(2.0);
69    /// assert!(matches!(
70    ///     result,
71    ///     Err(hoomd_utility::valid::Error::NotInOpenUnitInterval(_))
72    /// ));
73    /// ```
74    ///
75    /// # Errors
76    ///
77    /// [`Error::NotFinite`] when `v` is not finite.
78    /// [`Error::NotInOpenUnitInterval`] when `v` is not in (0,1).
79    #[inline]
80    fn try_from(v: f64) -> Result<OpenUnitIntervalNumber, Error> {
81        if !v.is_finite() {
82            Err(Error::NotFinite(v))
83        } else if v <= 0.0 || v >= 1.0 {
84            Err(Error::NotInOpenUnitInterval(v))
85        } else {
86            Ok(OpenUnitIntervalNumber(v))
87        }
88    }
89}
90
91impl fmt::Display for OpenUnitIntervalNumber {
92    #[inline]
93    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94        self.0.fmt(f)
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101    use assert2::check;
102
103    #[test]
104    fn open_unit_validation() {
105        let result = OpenUnitIntervalNumber::try_from(f64::INFINITY);
106        check!(result == Err(Error::NotFinite(f64::INFINITY)));
107
108        let result = OpenUnitIntervalNumber::try_from(-f64::INFINITY);
109        check!(result == Err(Error::NotFinite(-f64::INFINITY)));
110
111        let result = OpenUnitIntervalNumber::try_from(f64::NAN);
112        check!(matches!(result, Err(Error::NotFinite(_))));
113
114        let result = OpenUnitIntervalNumber::try_from(0.0);
115        check!(matches!(result, Err(Error::NotInOpenUnitInterval(_))));
116
117        let result = OpenUnitIntervalNumber::try_from(-1.0);
118        check!(matches!(result, Err(Error::NotInOpenUnitInterval(_))));
119
120        let result = OpenUnitIntervalNumber::try_from(1.0_f64.next_down());
121        check!(result == Ok(OpenUnitIntervalNumber(1.0_f64.next_down())));
122    }
123}