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 core::{
    cmp::Ordering,
    convert::TryFrom,
    fmt,
    hash::{Hash, Hasher},
    ops::Neg,
};

use serde::{Deserialize, Serialize};

use crate::NonNegativeF64;

#[derive(Debug)]
#[allow(clippy::module_name_repetitions)]
pub struct NonPositiveF64Error(f64);

impl fmt::Display for NonPositiveF64Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{} is positive.", self.0)
    }
}

#[allow(clippy::unsafe_derive_deserialize)]
#[derive(Copy, Clone, Serialize, Deserialize, TypeLayout)]
#[repr(transparent)]
#[serde(try_from = "f64", into = "f64")]
pub struct NonPositiveF64(f64);

impl fmt::Display for NonPositiveF64 {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, fmt)
    }
}

impl TryFrom<f64> for NonPositiveF64 {
    type Error = NonPositiveF64Error;

    fn try_from(value: f64) -> Result<Self, Self::Error> {
        Self::new(value)
    }
}

impl From<NonPositiveF64> for f64 {
    fn from(val: NonPositiveF64) -> Self {
        val.get()
    }
}

impl fmt::Debug for NonPositiveF64 {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        struct NonPositiveF64Range(f64);

        impl fmt::Debug for NonPositiveF64Range {
            fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
                write!(fmt, "{} <= 0.0", self.0)
            }
        }

        fmt.debug_tuple("NonPositiveF64")
            .field(&NonPositiveF64Range(self.0))
            .finish()
    }
}

impl NonPositiveF64 {
    /// # Errors
    ///
    /// Returns `NonPositiveF64Error` if not `value <= 0.0`
    pub const fn new(value: f64) -> Result<Self, NonPositiveF64Error> {
        if value <= 0.0 {
            Ok(Self(value))
        } else {
            Err(NonPositiveF64Error(value))
        }
    }

    /// # Safety
    ///
    /// Only safe iff `value <= 0.0`
    #[must_use]
    pub const unsafe fn new_unchecked(value: f64) -> Self {
        Self(value)
    }

    #[must_use]
    pub const fn zero() -> Self {
        Self(0.0_f64)
    }

    #[must_use]
    pub const fn get(self) -> f64 {
        self.0
    }
}

impl PartialEq for NonPositiveF64 {
    #[allow(clippy::unconditional_recursion)]
    fn eq(&self, other: &Self) -> bool {
        self.0.eq(&other.0)
    }
}

impl Eq for NonPositiveF64 {}

impl PartialOrd for NonPositiveF64 {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for NonPositiveF64 {
    fn cmp(&self, other: &Self) -> Ordering {
        self.0.total_cmp(&other.0)
    }
}

impl Hash for NonPositiveF64 {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.0.to_bits().hash(state);
    }
}

impl PartialEq<f64> for NonPositiveF64 {
    fn eq(&self, other: &f64) -> bool {
        self.0.eq(other)
    }
}

impl PartialOrd<f64> for NonPositiveF64 {
    fn partial_cmp(&self, other: &f64) -> Option<Ordering> {
        self.0.partial_cmp(other)
    }
}

impl Neg for NonPositiveF64 {
    type Output = NonNegativeF64;

    fn neg(self) -> Self::Output {
        unsafe { NonNegativeF64::new_unchecked(-self.0) }
    }
}