manifold3d_types/math/
positive_num.rs

1use crate::math;
2use std::cmp::Ordering;
3use thiserror::Error;
4
5#[derive(Error, Debug)]
6pub enum PositiveNumError {
7    #[error("the value is not positive")]
8    NonPositiveValue,
9}
10
11#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
12pub struct PositiveNum<T: num_traits::Num + Clone + Copy + PartialOrd>(T);
13
14impl<T: num_traits::Num + Copy + PartialOrd> PositiveNum<T> {
15    pub fn new(value: T) -> Result<Self, math::PositiveNumError> {
16        if !Self::is_valid(value) {
17            return Err(math::PositiveNumError::NonPositiveValue);
18        }
19        Ok(PositiveNum(value))
20    }
21
22    #[inline(always)]
23    fn is_valid(value: T) -> bool {
24        value > T::zero()
25    }
26}
27
28impl<T: num_traits::Num + PartialOrd + Copy> PositiveNum<T> {
29    pub fn get(&self) -> T {
30        self.0
31    }
32}
33
34macro_rules! impl_positive_num_from {
35    ($ttype:ident, $underlying_primitive:ident, ($($from_type:ty),+)) => {
36        $(
37            impl From<$from_type> for $ttype {
38                fn from(value: $from_type) -> Self {
39                    PositiveNum($underlying_primitive::from(value))
40                }
41            }
42        )+
43    };
44}
45
46macro_rules! impl_positive_num_try_from {
47    ($ttype:ident, $underlying_primitive:ident, ($($from_type:ty),+)) => {
48        $(
49            impl TryFrom<$from_type> for $ttype {
50                type Error = PositiveNumError;
51
52                fn try_from(value: $from_type) -> Result<Self, Self::Error> {
53                    $ttype::new($underlying_primitive::from(value))
54                }
55            }
56        )+
57    };
58}
59
60macro_rules! impl_into_primitive {
61    ($ttype:ident, $underlying_primitive:ident) => {
62        #[allow(clippy::from_over_into)]
63        impl Into<$underlying_primitive> for $ttype {
64            fn into(self) -> $underlying_primitive {
65                self.get()
66            }
67        }
68    };
69}
70
71macro_rules! impl_partial_eq {
72    ($ttype:ident, $underlying_primitive:ident, ($($eq_type:ty),+)) => {
73        $(
74            impl PartialEq<$eq_type> for $ttype {
75                fn eq(&self, other: &$eq_type) -> bool {
76                    self.get().eq(&(*other as $underlying_primitive))
77                }
78            }
79        )+
80    };
81}
82
83macro_rules! impl_partial_ord {
84    ($ttype:ident, $underlying_primitive:ident, ($($eq_type:ty),+)) => {
85        $(
86            impl PartialOrd<$eq_type> for $ttype {
87                fn partial_cmp(&self, other: &$eq_type) -> Option<Ordering> {
88                    self.get().partial_cmp(&(*other as $underlying_primitive))
89                }
90            }
91        )+
92    };
93}
94
95pub type PositiveI32 = PositiveNum<i32>;
96impl_into_primitive!(PositiveI32, i32);
97impl_positive_num_from!(PositiveI32, i32, (u8, u16));
98impl_positive_num_try_from!(PositiveI32, i32, (i8, i16, i32));
99impl_partial_eq!(PositiveI32, i32, (i8, i16, i32));
100impl_partial_ord!(PositiveI32, i32, (i8, i16, i32));
101
102pub type PositiveF64 = PositiveNum<f64>;
103impl_into_primitive!(PositiveF64, f64);
104impl_positive_num_from!(PositiveF64, f64, (u8, u16, u32));
105impl_positive_num_try_from!(PositiveF64, f64, (i8, i16, i32, f32, f64));
106impl_partial_eq!(PositiveF64, f64, (i8, i16, i32, f32, f64));
107impl_partial_ord!(PositiveF64, f64, (i8, i16, i32, f32, f64));