Skip to main content

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(
34    Debug,
35    Clone,
36    Copy,
37    Serialize,
38    Deserialize,
39    Eq,
40    PartialEq,
41    Hash,
42    Ord,
43    PartialOrd
44)]
45#[repr(transparent)]
46#[serde(transparent)]
47pub struct NonNeg<T>(T)
48where
49    T: Signed + fmt::Display;
50
51impl<T> NonNeg<T>
52where
53    T: Signed + fmt::Display,
54{
55    /// Returns the minimum value of the type.
56    pub fn min() -> NonNeg<T> {
57        NonNeg(T::zero())
58    }
59
60    /// Returns the maximum value of the type.
61    pub fn max() -> NonNeg<T>
62    where
63        T: UpperBounded,
64    {
65        NonNeg(T::max_value())
66    }
67
68    /// Attempts to construct a `NonNeg` from its underlying type.
69    ///
70    /// Returns an error if `n` is negative.
71    pub fn try_from(n: T) -> Result<NonNeg<T>, NonNegError> {
72        match n.is_negative() {
73            false => Ok(NonNeg(n)),
74            true => Err(NonNegError),
75        }
76    }
77}
78
79impl<T> fmt::Display for NonNeg<T>
80where
81    T: Signed + fmt::Display,
82{
83    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84        self.0.fmt(f)
85    }
86}
87
88impl<T> Deref for NonNeg<T>
89where
90    T: Signed + fmt::Display,
91{
92    type Target = T;
93
94    fn deref(&self) -> &T {
95        &self.0
96    }
97}
98
99impl From<NonNeg<i64>> for u64 {
100    fn from(n: NonNeg<i64>) -> u64 {
101        u64::try_from(*n).expect("non-negative")
102    }
103}
104
105#[cfg(target_pointer_width = "64")]
106impl crate::cast::CastFrom<NonNeg<i64>> for usize {
107    #[allow(clippy::as_conversions)]
108    fn cast_from(from: NonNeg<i64>) -> usize {
109        usize::cast_from(u64::from(from))
110    }
111}
112
113#[cfg(feature = "proptest")]
114impl<T> Arbitrary for NonNeg<T>
115where
116    T: Signed + UpperBounded + fmt::Display + fmt::Debug + Copy + 'static,
117    Range<T>: Strategy<Value = T>,
118{
119    type Strategy = BoxedStrategy<Self>;
120    type Parameters = ();
121
122    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
123        (*Self::min()..*Self::max()).prop_map(NonNeg).boxed()
124    }
125}
126
127/// An error indicating the attempted construction of a `NonNeg` with a negative
128/// number.
129#[derive(Debug, Clone)]
130pub struct NonNegError;
131
132impl fmt::Display for NonNegError {
133    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134        f.write_str("cannot construct NonNeg from negative number")
135    }
136}
137
138impl Error for NonNegError {}