From a2e98a943aa885ec91814bb2a95c84dbe877fab2 Mon Sep 17 00:00:00 2001
From: dharjeezy <dharjeezy@gmail.com>
Date: Thu, 3 Nov 2022 20:51:18 +0100
Subject: [PATCH] Introduce DefensiveMin and DefensiveMax (#12554)

* 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 <>
---
 substrate/frame/support/src/traits.rs      |  10 +-
 substrate/frame/support/src/traits/misc.rs | 176 +++++++++++++++++++++
 2 files changed, 181 insertions(+), 5 deletions(-)

diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs
index 2b6c5efee10..f09b715a970 100644
--- a/substrate/frame/support/src/traits.rs
+++ b/substrate/frame/support/src/traits.rs
@@ -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};
diff --git a/substrate/frame/support/src/traits/misc.rs b/substrate/frame/support/src/traits/misc.rs
index 1297956b2f6..ce8faaaf37c 100644
--- a/substrate/frame/support/src/traits/misc.rs
+++ b/substrate/frame/support/src/traits/misc.rs
@@ -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.
@@ -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);
+	}
 }
-- 
GitLab