use crate::traits::Elicitation;
use crate::verification::Contract;
#[derive(Debug, Clone, Copy)]
pub struct SurveyReturnsValidVariant<E> {
_phantom: std::marker::PhantomData<E>,
}
impl<E> SurveyReturnsValidVariant<E> {
pub const fn new() -> Self {
Self {
_phantom: std::marker::PhantomData,
}
}
}
impl<E> Default for SurveyReturnsValidVariant<E> {
fn default() -> Self {
Self::new()
}
}
impl<E> Contract for SurveyReturnsValidVariant<E>
where
E: Elicitation + Clone + std::fmt::Debug + Send + PartialEq,
{
type Input = E;
type Output = E;
fn requires(_input: &E) -> bool {
true
}
fn ensures(_input: &E, output: &E) -> bool {
let _ = output;
true
}
fn invariant(&self) -> bool {
true
}
}
#[derive(Debug, Clone, Copy)]
pub struct AffirmReturnsBoolean;
impl Contract for AffirmReturnsBoolean {
type Input = bool;
type Output = bool;
fn requires(_input: &bool) -> bool {
true
}
fn ensures(_input: &bool, _output: &bool) -> bool {
true
}
fn invariant(&self) -> bool {
true
}
}
#[derive(Debug, Clone, Copy)]
pub struct TextReturnsString;
impl Contract for TextReturnsString {
type Input = String;
type Output = String;
fn requires(_input: &String) -> bool {
true
}
fn ensures(_input: &String, output: &String) -> bool {
let _ = output;
true
}
fn invariant(&self) -> bool {
true
}
}
#[derive(Debug, Clone, Copy)]
pub struct TextReturnsNonEmpty;
impl Contract for TextReturnsNonEmpty {
type Input = String;
type Output = String;
fn requires(_input: &String) -> bool {
true
}
fn ensures(_input: &String, output: &String) -> bool {
!output.is_empty()
}
fn invariant(&self) -> bool {
true
}
}
#[derive(Debug, Clone, Copy)]
pub struct NumericReturnsValid<T> {
_phantom: std::marker::PhantomData<T>,
}
impl<T> NumericReturnsValid<T> {
pub const fn new() -> Self {
Self {
_phantom: std::marker::PhantomData,
}
}
}
impl<T> Default for NumericReturnsValid<T> {
fn default() -> Self {
Self::new()
}
}
macro_rules! impl_numeric_contract {
($t:ty) => {
impl Contract for NumericReturnsValid<$t> {
type Input = $t;
type Output = $t;
fn requires(_input: &$t) -> bool {
true
}
fn ensures(_input: &$t, output: &$t) -> bool {
*output >= <$t>::MIN && *output <= <$t>::MAX
}
fn invariant(&self) -> bool {
true
}
}
};
}
impl_numeric_contract!(i8);
impl_numeric_contract!(i16);
impl_numeric_contract!(i32);
impl_numeric_contract!(i64);
impl_numeric_contract!(i128);
impl_numeric_contract!(isize);
impl_numeric_contract!(u8);
impl_numeric_contract!(u16);
impl_numeric_contract!(u32);
impl_numeric_contract!(u64);
impl_numeric_contract!(u128);
impl_numeric_contract!(usize);
#[derive(Debug, Clone, Copy)]
pub struct MechanismWithType<M, T> {
pub mechanism: M,
pub type_contract: T,
}
impl<M, T> MechanismWithType<M, T> {
pub const fn new(mechanism: M, type_contract: T) -> Self {
Self {
mechanism,
type_contract,
}
}
}
impl<M, T, I, O> Contract for MechanismWithType<M, T>
where
M: Contract<Input = I, Output = O>,
T: Contract<Input = I, Output = O>,
I: Elicitation + Clone + std::fmt::Debug + Send,
O: Elicitation + Clone + std::fmt::Debug + Send,
{
type Input = I;
type Output = O;
fn requires(input: &I) -> bool {
M::requires(input) && T::requires(input)
}
fn ensures(input: &I, output: &O) -> bool {
M::ensures(input, output) &&
T::ensures(input, output)
}
fn invariant(&self) -> bool {
self.mechanism.invariant() && self.type_contract.invariant()
}
}
#[derive(Debug, Clone, Copy)]
pub struct InputNonEmpty;
impl Contract for InputNonEmpty {
type Input = String;
type Output = String;
fn requires(_input: &String) -> bool {
true
}
fn ensures(_input: &String, output: &String) -> bool {
!output.is_empty()
}
fn invariant(&self) -> bool {
true
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::verification::contracts::I32Positive;
#[test]
fn test_affirm_mechanism() {
let input = true;
assert!(AffirmReturnsBoolean::requires(&input));
assert!(AffirmReturnsBoolean::ensures(&input, &true));
assert!(AffirmReturnsBoolean::ensures(&input, &false));
}
#[test]
fn test_text_mechanism() {
let input = String::new();
assert!(TextReturnsString::requires(&input));
assert!(TextReturnsString::ensures(&input, &String::from("hello")));
assert!(TextReturnsString::ensures(&input, &String::new()));
}
#[test]
fn test_text_non_empty_mechanism() {
let input = String::new();
assert!(TextReturnsNonEmpty::requires(&input));
assert!(TextReturnsNonEmpty::ensures(&input, &String::from("hello")));
assert!(!TextReturnsNonEmpty::ensures(&input, &String::new()));
}
#[test]
fn test_numeric_mechanism() {
let input = 0i32;
assert!(NumericReturnsValid::<i32>::requires(&input));
assert!(NumericReturnsValid::<i32>::ensures(&input, &42));
assert!(NumericReturnsValid::<i32>::ensures(&input, &-1));
assert!(NumericReturnsValid::<i32>::ensures(&input, &i32::MIN));
assert!(NumericReturnsValid::<i32>::ensures(&input, &i32::MAX));
}
#[test]
fn test_mechanism_with_type_composition() {
let mechanism = NumericReturnsValid::<i32>::new();
let type_contract = I32Positive;
let contract = MechanismWithType::new(mechanism, type_contract);
let positive = 42i32;
assert!(
MechanismWithType::<NumericReturnsValid<i32>, I32Positive>::ensures(
&positive, &positive
)
);
let negative = -1i32;
assert!(
!MechanismWithType::<NumericReturnsValid<i32>, I32Positive>::ensures(
&negative, &negative
)
);
assert!(contract.invariant());
}
}