use num::traits::bounds::UpperBounded;
use num::Signed;
#[cfg(feature = "proptest")]
use proptest::arbitrary::Arbitrary;
#[cfg(feature = "proptest")]
use proptest::strategy::{BoxedStrategy, Strategy};
use serde::{Deserialize, Serialize};
use std::error::Error;
use std::fmt;
use std::ops::Deref;
#[cfg(feature = "proptest")]
use std::ops::Range;
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[repr(transparent)]
#[serde(transparent)]
pub struct NonNeg<T>(T)
where
T: Signed + fmt::Display;
impl<T> NonNeg<T>
where
T: Signed + fmt::Display,
{
pub fn min() -> NonNeg<T> {
NonNeg(T::zero())
}
pub fn max() -> NonNeg<T>
where
T: UpperBounded,
{
NonNeg(T::max_value())
}
pub fn try_from(n: T) -> Result<NonNeg<T>, NonNegError> {
match n.is_negative() {
false => Ok(NonNeg(n)),
true => Err(NonNegError),
}
}
}
impl<T> fmt::Display for NonNeg<T>
where
T: Signed + fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl<T> Deref for NonNeg<T>
where
T: Signed + fmt::Display,
{
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl From<NonNeg<i64>> for u64 {
fn from(n: NonNeg<i64>) -> u64 {
u64::try_from(*n).expect("non-negative")
}
}
#[cfg(target_pointer_width = "64")]
impl crate::cast::CastFrom<NonNeg<i64>> for usize {
#[allow(clippy::as_conversions)]
fn cast_from(from: NonNeg<i64>) -> usize {
usize::cast_from(u64::from(from))
}
}
#[cfg(feature = "proptest")]
impl<T> Arbitrary for NonNeg<T>
where
T: Signed + UpperBounded + fmt::Display + fmt::Debug + Copy + 'static,
Range<T>: Strategy<Value = T>,
{
type Strategy = BoxedStrategy<Self>;
type Parameters = ();
fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
(*Self::min()..*Self::max()).prop_map(NonNeg).boxed()
}
}
#[derive(Debug, Clone)]
pub struct NonNegError;
impl fmt::Display for NonNegError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("cannot construct NonNeg from negative number")
}
}
impl Error for NonNegError {}