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}