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

Introduce DefensiveMin and DefensiveMax #12554

Merged
Prev Previous commit
Next Next commit
defensive min and strict min with tests
  • Loading branch information
dharjeezy committed Oct 26, 2022
commit ecf7dfde2a3b590e5f00108777c1d65e9c958bfd
94 changes: 44 additions & 50 deletions frame/support/src/traits/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use codec::{CompactLen, Decode, DecodeLimit, Encode, EncodeLike, Input, MaxEncod
use impl_trait_for_tuples::impl_for_tuples;
use scale_info::{build::Fields, meta_type, Path, Type, TypeInfo, TypeParameter};
use sp_arithmetic::traits::{CheckedAdd, CheckedMul, CheckedSub, Saturating};
use sp_core::bounded::bounded_vec::{Min, Max, TruncateFrom};
use sp_core::bounded::bounded_vec::{Max, Min, TruncateFrom};
#[doc(hidden)]
pub use sp_runtime::traits::{
ConstBool, ConstI128, ConstI16, ConstI32, ConstI64, ConstI8, ConstU128, ConstU16, ConstU32,
Expand Down Expand Up @@ -408,62 +408,27 @@ where
}

pub trait DefensiveMin<T> {
fn defensive_min(unbound: T) -> Self;
fn defensive_strict_min(unbound: T) -> Self;
fn defensive_min(&self, t: T) -> Self;
fn defensive_strict_min(&self, t: T) -> Self;
}

impl<T, U> DefensiveMin<U> for T
where
T: Min<U> + TryFrom<U, Error = U>,
T: Min<U> + Clone,
U: Copy + std::fmt::Debug,
dharjeezy marked this conversation as resolved.
Show resolved Hide resolved
{
fn defensive_min(unbound: U) -> Self {
unbound.try_into().map_or_else(
|err| {
defensive!("DefensiveMin minimize");
T::min(err)
},
|bound| bound,
)
}

fn defensive_strict_min(unbound: U) -> Self {
unbound.try_into().map_or_else(
|err| {
defensive!("DefensiveStrictMin minimize");
T::strict_min(err)
},
|bound| bound,
)
}
}

pub trait DefensiveMax<T> {
fn defensive_max(unbound: T) -> Self;
fn defensive_strict_max(unbound: T) -> Self;
}

impl<T, U> DefensiveMax<U> for T
where
T: Max<U> + TryFrom<U, Error = U>,
{
fn defensive_max(unbound: U) -> Self {
unbound.try_into().map_or_else(
|err| {
defensive!("DefensiveMax maximize");
T::max(err)
},
|bound| bound,
)
fn defensive_min(&self, u: U) -> Self {
T::min(&self, u).unwrap_or_else(|err| {
defensive!("DefensiveMin minimize");
self.clone()
})
}

fn defensive_strict_max(unbound: U) -> Self {
unbound.try_into().map_or_else(
|err| {
defensive!("DefensiveStrictMax maximize");
T::strict_max(err)
},
|bound| bound,
)
fn defensive_strict_min(&self, u: U) -> Self {
T::strict_min(&self, u).unwrap_or_else(|err| {
defensive!("DefensiveStrictMin minimize");
self.clone()
})
}
}

Expand Down Expand Up @@ -1056,6 +1021,7 @@ mod test {
fn defensive_truncating_from_vec_defensive_works() {
let unbound = vec![1u32, 2];
let bound = BoundedVec::<u32, ConstU32<1>>::defensive_truncate_from(unbound);
//assert_eq!(10, 10_u32.defensive_min(11_u32));
assert_eq!(bound, vec![1u32]);
}

Expand Down Expand Up @@ -1169,4 +1135,32 @@ 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 minimize\"")]
fn defensive_min_panics() {
assert_eq!(9, 12_u32.defensive_min(9_u32));
assert_eq!(10, 11_u32.defensive_min(10_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!: \"DefensiveStrictMin minimize\""
)]
fn defensive_strict_min_panics() {
assert_eq!(9, 9_u32.defensive_strict_min(9_u32));
assert_eq!(10, 11_u32.defensive_strict_min(10_u32));
}
}
47 changes: 32 additions & 15 deletions primitives/core/src/bounded/bounded_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,17 @@ pub trait TruncateFrom<T> {
}

pub trait Min<T> {
fn min(unbound: T) -> Self;
fn strict_min(unbound: T) -> Self;
fn min(&self, t: T) -> Result<Self, T>
where
Self: Sized;
fn strict_min(&self, t: T) -> Result<Self, T>
where
Self: Sized;
}

pub trait Max<T> {
fn max(unbound: T) -> Self;
fn strict_max(unbound: T) -> Self;
fn max(&self, t: T) -> Self;
fn strict_max(&self, t: T) -> Self;
}

#[cfg(feature = "std")]
Expand Down Expand Up @@ -730,23 +734,36 @@ impl<T, S: Get<u32>> TruncateFrom<Vec<T>> for BoundedVec<T, S> {
}
}

impl<T, S: Get<u32>> Min<Vec<T>> for BoundedVec<T, S> {
fn min(unbound: Vec<T>) -> Self {
todo!()
impl<T, S: std::cmp::PartialOrd<T> + std::clone::Clone> Min<T> for S {
dharjeezy marked this conversation as resolved.
Show resolved Hide resolved
fn min(&self, t: T) -> Result<Self, T> {
if self <= &t {
Ok(self.clone())
} else {
Err(t)
}
}
fn strict_min(unbound: Vec<T>) -> Self {
todo!()

fn strict_min(&self, t: T) -> Result<Self, T> {
if self < &t {
Ok(self.clone())
} else {
Err(t)
}
}
}

impl<T, S: Get<u32>> Max<Vec<T>> for BoundedVec<T, S> {
fn max(unbound: Vec<T>) -> Self {
todo!()
/*impl<T, S> Max<T> for S {
fn max(&self, t: T) -> Self {
if &self >= &t {
self
}
}
fn strict_max(unbound: Vec<T>) -> Self {
todo!()
fn strict_max(&self, t: T) -> Self {
if &self > &t {
self
}
}
}
}*/

// It is okay to give a non-mutable reference of the inner vec to anyone.
impl<T, S> AsRef<Vec<T>> for BoundedVec<T, S> {
Expand Down