diff --git a/primitive-types/src/lib.rs b/primitive-types/src/lib.rs index 41b740298..39e4c829b 100644 --- a/primitive-types/src/lib.rs +++ b/primitive-types/src/lib.rs @@ -27,7 +27,7 @@ use core::convert::TryFrom; use fixed_hash::{construct_fixed_hash, impl_fixed_hash_conversions}; #[cfg(feature = "scale-info")] use scale_info_crate::TypeInfo; -use uint::{construct_uint, uint_full_mul_reg}; +use uint::{construct_uint, uint_full_mul_reg, Incrementable}; /// Error type for conversion. #[derive(Debug, PartialEq, Eq)] diff --git a/uint/examples/modular.rs b/uint/examples/modular.rs index 30b236992..341278092 100644 --- a/uint/examples/modular.rs +++ b/uint/examples/modular.rs @@ -8,6 +8,7 @@ #[macro_use] extern crate uint; +use uint::Incrementable; construct_uint! { pub struct U256(4); diff --git a/uint/src/uint.rs b/uint/src/uint.rs index bc4b7416c..c7073dcce 100644 --- a/uint/src/uint.rs +++ b/uint/src/uint.rs @@ -127,6 +127,45 @@ impl From for FromStrRadixErr { } } +macro_rules! impl_incrementable { + ($($type:ty),+) => { + $( + impl Incrementable for $type { + fn increment(&self) -> Option { + self.checked_add(1) + } + + fn initial_value() -> Option { + Some(0) + } + } + )+ + }; +} + +/// A trait representing an incrementable type. +/// +/// The `increment` and `initial_value` functions are fallible. +/// They should either both return `Some` with a valid value, or `None`. +pub trait Incrementable + where + Self: Sized, +{ + /// Increments the value. + /// + /// Returns `Some` with the incremented value if it is possible, or `None` if it is not. + fn increment(&self) -> Option; + + /// Returns the initial value. + /// + /// Returns `Some` with the initial value if it is available, or `None` if it is not. + fn initial_value() -> Option; +} + +impl_incrementable!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); + + + /// Conversion from decimal string error #[derive(Debug, PartialEq, Eq)] pub enum FromDecStrErr { @@ -493,6 +532,16 @@ macro_rules! construct_uint { } } + impl Incrementable for $name { + fn increment(&self) -> Option { + self.checked_add(Self::one()) + } + + fn initial_value() -> Option { + Some(Self::zero()) + } + } + impl $name { /// Low 2 words (u128) #[inline] diff --git a/uint/tests/uint_tests.rs b/uint/tests/uint_tests.rs index a830e488e..ebfd6aa9b 100644 --- a/uint/tests/uint_tests.rs +++ b/uint/tests/uint_tests.rs @@ -8,7 +8,7 @@ use core::{convert::TryInto, str::FromStr, u64::MAX}; use crunchy::unroll; -use uint::{construct_uint, overflowing, FromDecStrErr}; +use uint::{construct_uint, overflowing, FromDecStrErr, Incrementable}; construct_uint! { pub struct U256(4); @@ -1192,9 +1192,31 @@ fn bit_assign() { check(U256::MAX, U256::zero()); } +#[test] +fn increment() { + macro_rules! test_incrementable { + ($($type:ident),+) => { + $( + assert_eq!($type::from(0 as $type).increment(), Some(1 as $type)); + assert_eq!($type::max_value().increment(), None); + assert_eq!($type::initial_value(), Some(0 as $type)); + )+ + }; + } + test_incrementable!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); + + assert_eq!(U256::from(0).increment(), Some(1.into())); + assert_eq!(U256::max_value().increment(), None); + assert_eq!(U256::initial_value(), Some(0.into())); + + assert_eq!(U512::from(0).increment(), Some(1.into())); + assert_eq!(U512::max_value().increment(), None); + assert_eq!(U512::initial_value(), Some(0.into())); +} + #[cfg(feature = "quickcheck")] pub mod laws { - use super::construct_uint; + use super::{construct_uint, Incrementable}; macro_rules! uint_laws { ($mod_name:ident, $uint_ty:ident) => { mod $mod_name {