sigma-types 0.2.2

Types checked for an invariant.
Documentation
#![allow(
    clippy::allow_attributes,
    reason = "`alloc::format` only warns on some feature combinations"
)]

#[cfg(not(feature = "std"))]
extern crate alloc;

#[cfg(not(feature = "std"))]
extern crate std;

#[cfg(not(feature = "serde"))]
use serde_json as _;

use {
    crate::{NonNegative, Zero as _},
    core::cmp::Ordering,
    quickcheck::TestResult,
};

#[cfg(not(feature = "std"))]
use alloc::vec::Vec;

#[cfg(not(feature = "std"))]
#[allow(unused_imports, reason = "complicated namespace resolution")]
use alloc::format;

#[cfg(debug_assertions)]
use std::panic::catch_unwind;

const _CHECK_ZERO_IMPL_FOR_NON_NEGATIVE: NonNegative<u8> = NonNegative::ZERO;

quickcheck::quickcheck! {
    #[cfg(debug_assertions)]
    fn sorted_vec_non_strict(v: Vec<u8>) -> TestResult {
        type Sorted = crate::Sorted<Vec<u8>, true>;
        let actually_sorted = v.is_sorted();
        match catch_unwind(|| Sorted::new(v)) {
            Ok(ok) => {
                if actually_sorted {
                    // Testing `Deref` method syntax:
                    if ok.is_sorted() {
                        for _ in ok {}
                        TestResult::passed()
                    } else {
                        TestResult::error("sigma type not sorted")
                    }
                } else {
                    TestResult::error("not sorted but passed")
                }
            }
            Err(e) => {
                if actually_sorted {
                    TestResult::error(format!("sorted but failed: {e:#?}"))
                } else {
                    TestResult::passed()
                }
            }
        }
    }

    #[cfg(debug_assertions)]
    fn sorted_vec_strict(v: Vec<u8>) -> TestResult {
        type Sorted = crate::Sorted<Vec<u8>, false>;
        let actually_sorted = v.is_sorted_by(|a,b| matches!(a.cmp(b), Ordering::Less));
        match catch_unwind(|| Sorted::new(v)) {
            Ok(ok) => {
                if actually_sorted {
                    // Testing `Deref` method syntax:
                    if ok.is_sorted() {
                        for _ in ok {}
                        TestResult::passed()
                    } else {
                        TestResult::error("sigma type not sorted")
                    }
                } else {
                    TestResult::error("not sorted but passed")
                }
            }
            Err(e) => {
                if actually_sorted {
                    TestResult::error(format!("sorted but failed: {e:#?}"))
                } else {
                    TestResult::passed()
                }
            }
        }
    }

    #[cfg(debug_assertions)]
    fn unit_closed(f: f32) -> TestResult {
        type OnUnit = crate::OnUnit<f32, true, true>;
        let actually_on_unit = ((0.)..=1.).contains(&f);
        match catch_unwind(|| OnUnit::new(f)) {
            Ok(..) => {
                if actually_on_unit {
                    TestResult::passed()
                } else {
                    TestResult::error("not on unit but passed")
                }
            }
            Err(e) => {
                if actually_on_unit {
                    TestResult::error(format!("on unit but failed: {e:#?}"))
                } else {
                    TestResult::passed()
                }
            }
        }
    }

    #[cfg(debug_assertions)]
    fn unit_open(f: f32) -> TestResult {
        type OnUnit = crate::OnUnit<f32, false, false>;
        let actually_on_unit = (f > 0_f32) && (f < 1_f32);
        match catch_unwind(|| OnUnit::new(f)) {
            Ok(..) => {
                if actually_on_unit {
                    TestResult::passed()
                } else {
                    TestResult::error("not on unit but passed")
                }
            }
            Err(e) => {
                if actually_on_unit {
                    TestResult::error(format!("on unit but failed: {e:#?}"))
                } else {
                    TestResult::passed()
                }
            }
        }
    }

    fn try_sorted_vec_strict(v: Vec<u8>) -> TestResult {
        type Sorted = crate::Sorted<Vec<u8>, false>;
        let actually_sorted = v.is_sorted_by(|a,b| matches!(a.cmp(b), Ordering::Less));
        Sorted::try_new(v).map_or_else(
            || {
                if actually_sorted {
                    TestResult::error("sorted but failed")
                } else {
                    TestResult::passed()
                }
            },
            |some| {
                if actually_sorted {
                    // Testing `Deref` method syntax:
                    if some.is_sorted() {
                        for _ in some {}
                        TestResult::passed()
                    } else {
                        TestResult::error("sigma type not sorted")
                    }
                } else {
                    TestResult::error("not sorted but passed")
                }
            },
        )
    }

    #[cfg(debug_assertions)]
    fn i64_non_negative(i: i64) -> TestResult {
        type NonNegative = crate::NonNegative<i64>;
        let actually_non_negative = i >= 0;
        match catch_unwind(|| NonNegative::new(i)) {
            Ok(..) => {
                if actually_non_negative {
                    TestResult::passed()
                } else {
                    TestResult::error("negative but passed")
                }
            }
            Err(e) => {
                if actually_non_negative {
                    TestResult::error(format!("non-negative but failed: {e:#?}"))
                } else {
                    TestResult::passed()
                }
            }
        }
    }

    #[cfg(debug_assertions)]
    fn i64_positive(i: i64) -> TestResult {
        type Positive = crate::Positive<i64>;
        let actually_positive = i > 0;
        match catch_unwind(|| Positive::new(i)) {
            Ok(..) => {
                if actually_positive {
                    TestResult::passed()
                } else {
                    TestResult::error("non-positive but passed")
                }
            }
            Err(e) => {
                if actually_positive {
                    TestResult::error(format!("positive but failed: {e:#?}"))
                } else {
                    TestResult::passed()
                }
            }
        }
    }

    #[cfg(debug_assertions)]
    fn positive_also_non_negative(i: i64) -> () {
        type Positive = crate::Positive<i64>;
        type NonNegative = crate::NonNegative<i64>;
        let Some(positive) = Positive::try_new(i) else {
            return;
        };
        let _: &NonNegative = positive.also();
    }

    #[cfg(debug_assertions)]
    fn non_negative_try_also_positive(i: i64) -> TestResult {
        type NonNegative = crate::NonNegative<i64>;
        type Positive = crate::Positive<i64>;
        let Some(non_negative) = NonNegative::try_new(i) else {
            return TestResult::discard();
        };
        let maybe_also: Result<&Positive, _> = non_negative.try_also();
        match maybe_also {
            Ok(..) => if i == 0 { TestResult::error("Zero but passed") } else { TestResult::passed() },
            Err(e) => if i == 0 { TestResult::passed() } else { TestResult::error(format!("Positive but failed: {e}")) },
        }
    }

    #[cfg(debug_assertions)]
    #[cfg(feature = "serde")]
    fn serde_roundtrip_positive_i64(i: i64) -> TestResult {
        type Positive = crate::Positive<i64>;
        let Ok(positive) = catch_unwind(|| Positive::new(i)) else {
            return TestResult::discard();
        };
        let json = match serde_json::to_string(&positive) {
            Ok(ok) => ok,
            Err(e) => return TestResult::error(format!("Couldn't serialize {positive:#?}: {e}"))
        };
        let rust: Positive = match serde_json::from_str(&json) {
            Ok(ok) => ok,
            Err(e) => return TestResult::error(format!("Couldn't deserialize {json:#?}: {e}"))
        };
        if positive == rust {
            TestResult::passed()
        } else {
            TestResult::error(format!("{positive:#?} -> {json:#?} -> {rust:#?} =/= {positive:#?}"))
        }
    }

    #[cfg(feature = "serde")]
    fn serde_fallible_positive_i64(i: i64) -> TestResult {
        type Positive = crate::Positive<i64>;
        let actually_positive = i > 0;
        let json = match serde_json::to_string(&i) {
            Ok(ok) => ok,
            Err(e) => return TestResult::error(format!("Couldn't serialize {i:#?}: {e}"))
        };
        let rust: Positive = match serde_json::from_str(&json) {
            Ok(ok) => {
                if actually_positive {
                    ok
                } else {
                    return TestResult::error(format!("Not positive, but deserialization succeeded: {i}"));
                }
            }
            Err(e) => {
                return if actually_positive {
                    TestResult::error(format!("Couldn't deserialize {json:#?}: {e}"))
                } else {
                    TestResult::passed()
                };
            }
        };
        if i == *rust.as_ref() {
            TestResult::passed()
        } else {
            TestResult::error(format!("{i:#?} -> {json:#?} -> {rust:#?} =/= {i:#?}"))
        }
    }
}