1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! This library provides [`condition::Condition`](#) a trait for
//! easier expression (and consumption) of features, toggles, checkboxes,
//! settings, options, or any other [*bivalent*][1] pair.
//!
//! NOTE: This crate will eventually support no-std, but does not at this time
//!
//! Conditions typically come in pairs. (e.g., `{Allow, Deny}`, `{Yes, No}`,
//! `{With, Without}`. This library provides several types by default. To
//! import them, use the [`prelude`](prelude/index.html) module. If you only want to use
//! the trait, simply import it.
//!
//! Use `impl Condition` anywhere you might take a boolean value. Then, use
//! any type that implements this condition.
//!
//! Within the function, branch off of the condition provided
//!
//! ```
//! use condition::prelude::*;
//!
//! #[derive(Condition)]
//! enum Answer {
//!   No,
//!   Yes
//! }
//!
//! pub fn verbose(v: impl Condition) { assert!(v.is(false)); }
//!
//! /* ... */
//!
//! use Answer::No;
//! verbose(No);
//!
//! ```
//!
//!
//! [1]: https://en.wikipedia.org/wiki/Principle_of_bivalence
//!

pub trait Condition: Sized {
  /// Checks if the Condition is in the same equivalent state as the given
  /// boolean. Everything else regarding a condition can be implemented in
  /// these terms
  fn is(&self, value: bool) -> bool;

  #[inline]
  fn is_false (&self) -> bool { self.is(false) }
  #[inline]
  fn is_true (&self) -> bool { self.is(true) }

  #[must_use]
  fn option (&self) -> Option<()> { if self.is(true) { Some(()) } else { None } }
  #[must_use]
  fn result (&self) -> Result<(), ()> { self.option().ok_or(()) }
}

impl<T> Condition for Option<T> {
  #[inline]
  fn is (&self, value: bool) -> bool { self.is_some().is(value) }
}

impl<T, E> Condition for Result<T, E> {
  #[inline]
  fn is (&self, value: bool) -> bool { self.is_ok().is(value) }
}

impl Condition for bool {
  #[inline]
  fn is (&self, value: bool) -> bool { *self == value }
}

#[cfg(feature = "std")]
impl Condition for std::process::ExitStatus {
  #[inline]
  fn is (&self, value: bool) -> bool { self.success().is(value) }
}

pub mod prelude {
  pub use ::condition_derive::*;
  pub use crate::Condition;
}

#[cfg(test)]
mod test {
  use super::prelude::*;
  #[derive(Condition)]
  enum Answer {
    No,
    Yes,
  }

  #[test]
  fn test_derive () {
    let no: Answer = Answer::No;
    let yes: Answer = Answer::Yes;
    assert!(no.is(false));
    assert!(yes.is(true));
  }

  #[test]
  fn test_option_condition () {
    assert!(Option::<()>::None.is(false));
    assert!(Option::Some(()).is(true));
  }

  #[test]
  fn test_result_condition () {
    assert!(Result::<(),()>::Err(()).is(false));
    assert!(Result::<(),()>::Ok(()).is(true));
  }

  #[test]
  fn test_bool_condition () {
    assert!(false.is(false));
    assert!(true.is(true));
  }
  #[test]
  fn test_into_option () {
    assert_eq!(Option::None, false.option());
    assert_eq!(Some(()), true.option());
  }

  #[test]
  fn test_into_result () {
    assert_eq!(Result::Err(()), false.result());
    assert_eq!(Result::Ok(()), true.result());
  }
}