1use num_traits::Float;
23use std::fmt;
24
25#[cfg(feature = "serde")]
26use serde::{Deserialize, Serialize};
27
28#[derive(Debug, Clone, PartialEq, Eq)]
30pub enum NonNegativeError {
31 InvalidValue,
33}
34
35impl fmt::Display for NonNegativeError {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 match self {
38 NonNegativeError::InvalidValue => write!(f, "Value must be non-negative and finite"),
39 }
40 }
41}
42
43impl std::error::Error for NonNegativeError {}
44
45#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
47#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
48pub struct NonNegative<T: Float>(T);
49
50impl<T: Float> NonNegative<T> {
51 pub fn zero() -> Self
53 where
54 T: num_traits::Zero,
55 {
56 Self(T::zero())
57 }
58
59 pub fn try_new(value: T) -> Result<Self, NonNegativeError> {
63 if value >= T::zero() && value.is_finite() {
64 Ok(Self(value))
65 } else {
66 Err(NonNegativeError::InvalidValue)
67 }
68 }
69
70 pub fn new(value: T) -> Self {
76 Self::try_new(value).expect("Value must be non-negative and finite")
77 }
78
79 pub fn get(&self) -> T {
81 self.0
82 }
83}
84
85impl<T: Float + num_traits::Zero> Default for NonNegative<T> {
86 fn default() -> Self {
87 Self::zero()
88 }
89}
90
91impl<T: Float + fmt::Display> fmt::Display for NonNegative<T> {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 self.0.fmt(f)
94 }
95}
96
97#[macro_export]
106macro_rules! nonneg {
107 ($t:ty) => {
108 $crate::NonNegative::<$t>::zero()
109 };
110 ($val:expr) => {{ $crate::NonNegative::try_new($val) }};
111 ($t:ty, $val:expr) => {{ $crate::NonNegative::<$t>::try_new($val) }};
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn test_zero() {
120 let zero = NonNegative::<f64>::zero();
121 assert_eq!(zero.get(), 0.0);
122 let default: NonNegative<f64> = Default::default();
123 assert_eq!(default.get(), 0.0);
124 }
125
126 #[test]
127 fn test_try_new_valid() {
128 let val = NonNegative::try_new(3.14f64).unwrap();
129 assert_eq!(val.get(), 3.14);
130 }
131
132 #[test]
133 fn test_try_new_invalid() {
134 assert_eq!(
135 NonNegative::try_new(-0.1f64).unwrap_err(),
136 NonNegativeError::InvalidValue
137 );
138 assert_eq!(
139 NonNegative::try_new(f64::NAN).unwrap_err(),
140 NonNegativeError::InvalidValue
141 );
142 assert_eq!(
143 NonNegative::try_new(f64::INFINITY).unwrap_err(),
144 NonNegativeError::InvalidValue
145 );
146 }
147
148 #[test]
149 fn test_new_panics() {
150 let _ = NonNegative::new(1.0f64); }
152
153 #[test]
154 #[should_panic(expected = "Value must be non-negative and finite")]
155 fn test_new_panics_on_invalid() {
156 let _ = NonNegative::new(-1.0f64);
157 }
158
159 #[test]
160 fn test_macro() {
161 let a = nonneg!(5.0f64).unwrap();
162 assert_eq!(a.get(), 5.0);
163
164 let b = nonneg!(f64);
165 assert_eq!(b.get(), 0.0);
166
167 let c = nonneg!(f32, 2.71).unwrap();
168 assert_eq!(c.get(), 2.71);
169
170 let d = nonneg!(-1.0f64);
171 assert!(d.is_err());
172 }
173}