Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
Introduce DefensiveMin and DefensiveMax (#12554)
Browse files Browse the repository at this point in the history
* traits for defensive min and defensive max

* defensive min and strict min with tests

* defensive max and strict max with tests

* include docs

* implement partial ord on defensive min and max

* Update frame/support/src/traits/misc.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* wrap lines

* Fix traits

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update frame/support/src/traits/misc.rs

* Update frame/support/src/traits/misc.rs

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: parity-processbot <>
  • Loading branch information
dharjeezy and ggwpez authored Nov 3, 2022
1 parent 87f3fde commit 564cdeb
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 5 deletions.
10 changes: 5 additions & 5 deletions frame/support/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ mod misc;
pub use misc::{
defensive_prelude::{self, *},
Backing, ConstBool, ConstI128, ConstI16, ConstI32, ConstI64, ConstI8, ConstU128, ConstU16,
ConstU32, ConstU64, ConstU8, DefensiveSaturating, DefensiveTruncateFrom,
EnsureInherentsAreFirst, EqualPrivilegeOnly, EstimateCallFee, ExecuteBlock, ExtrinsicCall, Get,
GetBacking, GetDefault, HandleLifetime, IsSubType, IsType, Len, OffchainWorker,
OnKilledAccount, OnNewAccount, PrivilegeCmp, SameOrOther, Time, TryCollect, TryDrop, TypedGet,
UnixTime, WrapperKeepOpaque, WrapperOpaque,
ConstU32, ConstU64, ConstU8, DefensiveMax, DefensiveMin, DefensiveSaturating,
DefensiveTruncateFrom, EnsureInherentsAreFirst, EqualPrivilegeOnly, EstimateCallFee,
ExecuteBlock, ExtrinsicCall, Get, GetBacking, GetDefault, HandleLifetime, IsSubType, IsType,
Len, OffchainWorker, OnKilledAccount, OnNewAccount, PrivilegeCmp, SameOrOther, Time,
TryCollect, TryDrop, TypedGet, UnixTime, WrapperKeepOpaque, WrapperOpaque,
};
#[allow(deprecated)]
pub use misc::{PreimageProvider, PreimageRecipient};
Expand Down
176 changes: 176 additions & 0 deletions frame/support/src/traits/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,134 @@ where
}
}

/// Defensively calculates the minimum of two values.
///
/// Can be used in contexts where we assume the receiver value to be (strictly) smaller.
pub trait DefensiveMin<T> {
/// Returns the minimum and defensively checks that `self` is not larger than `other`.
///
/// # Example
///
/// ```
/// use frame_support::traits::DefensiveMin;
/// // min(3, 4) is 3.
/// assert_eq!(3, 3_u32.defensive_min(4_u32));
/// // min(4, 4) is 4.
/// assert_eq!(4, 4_u32.defensive_min(4_u32));
/// ```
///
/// ```should_panic
/// use frame_support::traits::DefensiveMin;
/// // min(4, 3) panics.
/// 4_u32.defensive_min(3_u32);
/// ```
fn defensive_min(self, other: T) -> Self;

/// Returns the minimum and defensively checks that `self` is smaller than `other`.
///
/// # Example
///
/// ```
/// use frame_support::traits::DefensiveMin;
/// // min(3, 4) is 3.
/// assert_eq!(3, 3_u32.defensive_strict_min(4_u32));
/// ```
///
/// ```should_panic
/// use frame_support::traits::DefensiveMin;
/// // min(4, 4) panics.
/// 4_u32.defensive_strict_min(4_u32);
/// ```
fn defensive_strict_min(self, other: T) -> Self;
}

impl<T> DefensiveMin<T> for T
where
T: sp_std::cmp::PartialOrd<T>,
{
fn defensive_min(self, other: T) -> Self {
if self <= other {
self
} else {
defensive!("DefensiveMin");
other
}
}

fn defensive_strict_min(self, other: T) -> Self {
if self < other {
self
} else {
defensive!("DefensiveMin strict");
other
}
}
}

/// Defensively calculates the maximum of two values.
///
/// Can be used in contexts where we assume the receiver value to be (strictly) larger.
pub trait DefensiveMax<T> {
/// Returns the maximum and defensively asserts that `other` is not larger than `self`.
///
/// # Example
///
/// ```
/// use frame_support::traits::DefensiveMax;
/// // max(4, 3) is 4.
/// assert_eq!(4, 4_u32.defensive_max(3_u32));
/// // max(4, 4) is 4.
/// assert_eq!(4, 4_u32.defensive_max(4_u32));
/// ```
///
/// ```should_panic
/// use frame_support::traits::DefensiveMax;
/// // max(4, 5) panics.
/// 4_u32.defensive_max(5_u32);
/// ```
fn defensive_max(self, other: T) -> Self;

/// Returns the maximum and defensively asserts that `other` is smaller than `self`.
///
/// # Example
///
/// ```
/// use frame_support::traits::DefensiveMax;
/// // y(4, 3) is 4.
/// assert_eq!(4, 4_u32.defensive_strict_max(3_u32));
/// ```
///
/// ```should_panic
/// use frame_support::traits::DefensiveMax;
/// // max(4, 4) panics.
/// 4_u32.defensive_strict_max(4_u32);
/// ```
fn defensive_strict_max(self, other: T) -> Self;
}

impl<T> DefensiveMax<T> for T
where
T: sp_std::cmp::PartialOrd<T>,
{
fn defensive_max(self, other: T) -> Self {
if self >= other {
self
} else {
defensive!("DefensiveMax");
other
}
}

fn defensive_strict_max(self, other: T) -> Self {
if self > other {
self
} else {
defensive!("DefensiveMax strict");
other
}
}
}

/// Anything that can have a `::len()` method.
pub trait Len {
/// Return the length of data type.
Expand Down Expand Up @@ -1109,4 +1237,52 @@ mod test {
let data = decoded.encode();
WrapperOpaque::<u32>::decode(&mut &data[..]).unwrap();
}

#[test]
fn defensive_min_works() {
assert_eq!(10, 10_u32.defensive_min(11_u32));
assert_eq!(10, 10_u32.defensive_min(10_u32));
}

#[test]
#[should_panic(expected = "Defensive failure has been triggered!: \"DefensiveMin\"")]
fn defensive_min_panics() {
10_u32.defensive_min(9_u32);
}

#[test]
fn defensive_strict_min_works() {
assert_eq!(10, 10_u32.defensive_strict_min(11_u32));
assert_eq!(9, 9_u32.defensive_strict_min(10_u32));
}

#[test]
#[should_panic(expected = "Defensive failure has been triggered!: \"DefensiveMin strict\"")]
fn defensive_strict_min_panics() {
9_u32.defensive_strict_min(9_u32);
}

#[test]
fn defensive_max_works() {
assert_eq!(11, 11_u32.defensive_max(10_u32));
assert_eq!(10, 10_u32.defensive_max(10_u32));
}

#[test]
#[should_panic(expected = "Defensive failure has been triggered!: \"DefensiveMax\"")]
fn defensive_max_panics() {
9_u32.defensive_max(10_u32);
}

#[test]
fn defensive_strict_max_works() {
assert_eq!(11, 11_u32.defensive_strict_max(10_u32));
assert_eq!(10, 10_u32.defensive_strict_max(9_u32));
}

#[test]
#[should_panic(expected = "Defensive failure has been triggered!: \"DefensiveMax strict\"")]
fn defensive_strict_max_panics() {
9_u32.defensive_strict_max(9_u32);
}
}

0 comments on commit 564cdeb

Please sign in to comment.