mz_ore/
num.rs

1// Copyright 2019 The Rust Project Contributors
2// Copyright Materialize, Inc. and contributors. All rights reserved.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License in the LICENSE file at the
7// root of this repository, or online at
8//
9//     http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17//! Number utilities
18
19use num::Signed;
20use num::traits::bounds::UpperBounded;
21#[cfg(feature = "proptest")]
22use proptest::arbitrary::Arbitrary;
23#[cfg(feature = "proptest")]
24use proptest::strategy::{BoxedStrategy, Strategy};
25use serde::{Deserialize, Serialize};
26use std::error::Error;
27use std::fmt;
28use std::ops::Deref;
29#[cfg(feature = "proptest")]
30use std::ops::Range;
31
32/// A wrapper type which ensures a signed number is non-negative.
33#[derive(Debug, Clone, Copy, Serialize, Deserialize, Eq, PartialEq, Hash, Ord, PartialOrd)]
34#[repr(transparent)]
35#[serde(transparent)]
36pub struct NonNeg<T>(T)
37where
38    T: Signed + fmt::Display;
39
40impl<T> NonNeg<T>
41where
42    T: Signed + fmt::Display,
43{
44    /// Returns the minimum value of the type.
45    pub fn min() -> NonNeg<T> {
46        NonNeg(T::zero())
47    }
48
49    /// Returns the maximum value of the type.
50    pub fn max() -> NonNeg<T>
51    where
52        T: UpperBounded,
53    {
54        NonNeg(T::max_value())
55    }
56
57    /// Attempts to construct a `NonNeg` from its underlying type.
58    ///
59    /// Returns an error if `n` is negative.
60    pub fn try_from(n: T) -> Result<NonNeg<T>, NonNegError> {
61        match n.is_negative() {
62            false => Ok(NonNeg(n)),
63            true => Err(NonNegError),
64        }
65    }
66}
67
68impl<T> fmt::Display for NonNeg<T>
69where
70    T: Signed + fmt::Display,
71{
72    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73        self.0.fmt(f)
74    }
75}
76
77impl<T> Deref for NonNeg<T>
78where
79    T: Signed + fmt::Display,
80{
81    type Target = T;
82
83    fn deref(&self) -> &T {
84        &self.0
85    }
86}
87
88impl From<NonNeg<i64>> for u64 {
89    fn from(n: NonNeg<i64>) -> u64 {
90        u64::try_from(*n).expect("non-negative")
91    }
92}
93
94#[cfg(target_pointer_width = "64")]
95impl crate::cast::CastFrom<NonNeg<i64>> for usize {
96    #[allow(clippy::as_conversions)]
97    fn cast_from(from: NonNeg<i64>) -> usize {
98        usize::cast_from(u64::from(from))
99    }
100}
101
102#[cfg(feature = "proptest")]
103impl<T> Arbitrary for NonNeg<T>
104where
105    T: Signed + UpperBounded + fmt::Display + fmt::Debug + Copy + 'static,
106    Range<T>: Strategy<Value = T>,
107{
108    type Strategy = BoxedStrategy<Self>;
109    type Parameters = ();
110
111    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
112        (*Self::min()..*Self::max()).prop_map(NonNeg).boxed()
113    }
114}
115
116/// An error indicating the attempted construction of a `NonNeg` with a negative
117/// number.
118#[derive(Debug, Clone)]
119pub struct NonNegError;
120
121impl fmt::Display for NonNegError {
122    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
123        f.write_str("cannot construct NonNeg from negative number")
124    }
125}
126
127impl Error for NonNegError {}