Skip to main content

augurs_prophet/
positive_float.rs

1/// A positive-only, 64 bit precision floating point number.
2#[repr(transparent)]
3#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
4#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
5#[cfg_attr(feature = "serde", derive(serde::Serialize))]
6pub struct PositiveFloat(f64);
7
8/// An invalid float was provided when trying to create a [`PositiveFloat`].
9#[derive(Debug, thiserror::Error)]
10#[error("invalid float provided: {0}")]
11pub struct TryFromFloatError(f64);
12
13impl PositiveFloat {
14    /// Attempt to create a new `PositiveFloat`.
15    ///
16    /// # Errors
17    ///
18    /// Returns an error if the provided float is not finite or less than or equal to 0.0.
19    pub fn try_new(f: f64) -> Result<Self, TryFromFloatError> {
20        if !f.is_finite() || f <= 0.0 {
21            return Err(TryFromFloatError(f));
22        }
23        Ok(Self(f))
24    }
25
26    /// Create a new `PositiveFloat` with the value 1.0.
27    pub const fn one() -> Self {
28        Self(1.0)
29    }
30}
31
32impl TryFrom<f64> for PositiveFloat {
33    type Error = TryFromFloatError;
34    fn try_from(value: f64) -> Result<Self, Self::Error> {
35        Self::try_new(value)
36    }
37}
38
39impl std::ops::Deref for PositiveFloat {
40    type Target = f64;
41    fn deref(&self) -> &Self::Target {
42        &self.0
43    }
44}
45
46impl From<PositiveFloat> for f64 {
47    fn from(value: PositiveFloat) -> Self {
48        value.0
49    }
50}
51
52#[cfg(feature = "serde")]
53impl<'de> serde::Deserialize<'de> for PositiveFloat {
54    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
55    where
56        D: serde::Deserializer<'de>,
57    {
58        let f = f64::deserialize(deserializer)?;
59        Self::try_new(f).map_err(serde::de::Error::custom)
60    }
61}