diff --git a/src/rule.rs b/src/rule.rs index 14c4533..9789c71 100644 --- a/src/rule.rs +++ b/src/rule.rs @@ -1,3 +1,4 @@ +use crate::result::Error; pub use collection::*; pub use empty::*; pub use length::*; @@ -18,3 +19,27 @@ pub trait Rule { type Item; fn validate(target: Self::Item) -> crate::Result; } + +/// This is a `Rule` that always returns `Ok` +pub struct Valid { + _phantom: std::marker::PhantomData, +} + +impl Rule for Valid { + type Item = T; + fn validate(target: Self::Item) -> crate::Result { + Ok(target) + } +} + +/// This is a `Rule` that always returns `Err` +pub struct Invalid { + _phantom: std::marker::PhantomData, +} + +impl Rule for Invalid { + type Item = T; + fn validate(target: Self::Item) -> crate::Result { + Err(Error::new(target, "Invalid")) + } +} diff --git a/src/rule/composer.rs b/src/rule/composer.rs index 51abfdf..9157420 100644 --- a/src/rule/composer.rs +++ b/src/rule/composer.rs @@ -1,7 +1,17 @@ mod and; +mod equiv; +mod imply; +mod nand; +mod nor; mod not; mod or; +mod xor; pub use and::And; +pub use equiv::Equiv; +pub use imply::Imply; +pub use nand::Nand; +pub use nor::Nor; pub use not::Not; pub use or::Or; +pub use xor::Xor; diff --git a/src/rule/composer/equiv.rs b/src/rule/composer/equiv.rs new file mode 100644 index 0000000..7d45c0b --- /dev/null +++ b/src/rule/composer/equiv.rs @@ -0,0 +1,47 @@ +use crate::rule::composer::imply::Imply; +use crate::And; + +/// This is a type that represents logical equivalence in logic. +/// +/// # Example +/// ```rust +/// use refined_type::rule::composer::Equiv; +/// use refined_type::rule::{EvenRuleI8, GreaterEqualRuleI8, Rule}; +/// +/// type Target = Equiv, EvenRuleI8>; +/// +/// for value in vec![1, 10] { +/// assert!(Target::validate(value).is_ok()); +/// } +/// +/// for value in vec![2, 4] { +/// assert!(Target::validate(value).is_err()); +/// } +/// ``` +pub type Equiv = And![Imply, Imply]; + +#[cfg(test)] +mod test { + use crate::rule::composer::Equiv; + use crate::rule::{EvenRuleI8, GreaterEqualRuleI8, Rule}; + + type Target = Equiv, EvenRuleI8>; + + #[test] + fn test_rule_binder_ok() { + let table = vec![1, 10]; + + for value in table { + assert!(Target::validate(value).is_ok()); + } + } + + #[test] + fn test_rule_binder_err() { + let table = vec![2, 4]; + + for value in table { + assert!(Target::validate(value).is_err()); + } + } +} diff --git a/src/rule/composer/imply.rs b/src/rule/composer/imply.rs new file mode 100644 index 0000000..16dc54e --- /dev/null +++ b/src/rule/composer/imply.rs @@ -0,0 +1,46 @@ +use crate::rule::composer::Not; +use crate::Or; + +/// This is a type that represents logical implication in logic. +/// By applying it to programming, you can make it function similarly to an “If-Then” statement. +/// # Example +/// ```rust +/// use refined_type::rule::composer::Imply; +/// use refined_type::rule::{EvenRuleI8, GreaterEqualRuleI8, Rule}; +/// +/// type IfGreaterOrEqual10ThenEven = Imply, EvenRuleI8>; +/// +/// for value in vec![8, 9, 10, 12] { +/// assert!(IfGreaterOrEqual10ThenEven::validate(value).is_ok()); +/// } +/// +/// for value in vec![11, 13] { +/// assert!(IfGreaterOrEqual10ThenEven::validate(value).is_err()); +/// } +pub type Imply = Or![Not, RULE2]; + +#[cfg(test)] +mod test { + use crate::rule::composer::Imply; + use crate::rule::{EvenRuleI8, GreaterEqualRuleI8, Rule}; + + type IfGreaterOrEqual10ThenEven = Imply, EvenRuleI8>; + + #[test] + fn test_rule_binder_ok() { + let table = vec![8, 9, 10, 12]; + + for value in table { + assert!(IfGreaterOrEqual10ThenEven::validate(value).is_ok()); + } + } + + #[test] + fn test_rule_binder_err() { + let table = vec![11, 13]; + + for value in table { + assert!(IfGreaterOrEqual10ThenEven::validate(value).is_err()); + } + } +} diff --git a/src/rule/composer/nand.rs b/src/rule/composer/nand.rs new file mode 100644 index 0000000..1905026 --- /dev/null +++ b/src/rule/composer/nand.rs @@ -0,0 +1,74 @@ +use crate::rule::composer::Not; +use crate::And; + +/// This is a type that represents logical NAND in logic. +pub type Nand = Not; + +#[macro_export] +macro_rules! Nand { + ($rule1:ty, $rule2:ty) => { + $crate::rule::composer::Nand<$rule1, $rule2> + }; + + ($rule1:ty, $($rule2: ty), +) => { + $crate::rule::composer::Nand<$rule1, $crate::And![$($rule2), +]> + } +} + +#[cfg(test)] +mod test_2 { + use crate::rule::{Invalid, Rule, Valid}; + + type ValidI8 = Valid; + type InvalidI8 = Invalid; + + type Target1 = Nand![ValidI8, ValidI8]; // ERR + type Target2 = Nand![ValidI8, InvalidI8]; // PASS + type Target3 = Nand![InvalidI8, ValidI8]; // PASS + type Target4 = Nand![InvalidI8, InvalidI8]; // PASS + + #[test] + fn test_rule_binder_ok() { + assert!(Target2::validate(0).is_ok()); + assert!(Target3::validate(0).is_ok()); + assert!(Target4::validate(0).is_ok()); + } + + #[test] + fn test_rule_binder_err() { + assert!(Target1::validate(0).is_err()); + } +} + +#[cfg(test)] +mod test_3 { + use crate::rule::{Invalid, Rule, Valid}; + + type ValidI8 = Valid; + type InvalidI8 = Invalid; + + type Target1 = Nand![ValidI8, ValidI8, ValidI8]; // ERR + type Target2 = Nand![ValidI8, ValidI8, InvalidI8]; // PASS + type Target3 = Nand![ValidI8, InvalidI8, ValidI8]; // PASS + type Target4 = Nand![ValidI8, InvalidI8, InvalidI8]; // PASS + type Target5 = Nand![InvalidI8, ValidI8, ValidI8]; // PASS + type Target6 = Nand![InvalidI8, ValidI8, InvalidI8]; // PASS + type Target7 = Nand![InvalidI8, InvalidI8, ValidI8]; // PASS + type Target8 = Nand![InvalidI8, InvalidI8, InvalidI8]; // PASS + + #[test] + fn test_rule_binder_ok() { + assert!(Target2::validate(0).is_ok()); + assert!(Target3::validate(0).is_ok()); + assert!(Target4::validate(0).is_ok()); + assert!(Target5::validate(0).is_ok()); + assert!(Target6::validate(0).is_ok()); + assert!(Target7::validate(0).is_ok()); + assert!(Target8::validate(0).is_ok()); + } + + #[test] + fn test_rule_binder_err() { + assert!(Target1::validate(0).is_err()); + } +} diff --git a/src/rule/composer/nor.rs b/src/rule/composer/nor.rs new file mode 100644 index 0000000..644e037 --- /dev/null +++ b/src/rule/composer/nor.rs @@ -0,0 +1,74 @@ +use crate::Or; +use crate::rule::composer::Not; + +/// This is a type that represents logical NOR in logic. +pub type Nor = Not; + +#[macro_export] +macro_rules! Nor { + ($rule1:ty, $rule2:ty) => { + $crate::rule::composer::Nor<$rule1, $rule2> + }; + + ($rule1:ty, $($rule2: ty), +) => { + $crate::rule::composer::Nor<$rule1, $crate::Or![$($rule2), +]> + } +} + +#[cfg(test)] +mod test_2 { + use crate::rule::{Invalid, Rule, Valid}; + + type ValidI8 = Valid; + type InvalidI8 = Invalid; + + type Target1 = Nor![ValidI8, ValidI8]; // 2: ERR + type Target2 = Nor![ValidI8, InvalidI8]; // 1: ERR + type Target3 = Nor![InvalidI8, ValidI8]; // 1: ERR + type Target4 = Nor![InvalidI8, InvalidI8]; // 0: PASS + + #[test] + fn test_rule_binder_ok() { + assert!(Target4::validate(0).is_ok()); + } + + #[test] + fn test_rule_binder_err() { + assert!(Target1::validate(0).is_err()); + assert!(Target2::validate(0).is_err()); + assert!(Target3::validate(0).is_err()); + } +} + +#[cfg(test)] +mod test_3 { + use crate::rule::{Invalid, Rule, Valid}; + + type ValidI8 = Valid; + type InvalidI8 = Invalid; + + type Target1 = Nor![ValidI8, ValidI8, ValidI8]; // 3: ERR + type Target2 = Nor![ValidI8, ValidI8, InvalidI8]; // 2: ERR + type Target3 = Nor![ValidI8, InvalidI8, ValidI8]; // 2: ERR + type Target4 = Nor![ValidI8, InvalidI8, InvalidI8]; // 1: ERR + type Target5 = Nor![InvalidI8, ValidI8, ValidI8]; // 2: ERR + type Target6 = Nor![InvalidI8, ValidI8, InvalidI8]; // 1: ERR + type Target7 = Nor![InvalidI8, InvalidI8, ValidI8]; // 1: ERR + type Target8 = Nor![InvalidI8, InvalidI8, InvalidI8]; // 0: PASS + + #[test] + fn test_rule_binder_ok() { + assert!(Target8::validate(0).is_ok()); + } + + #[test] + fn test_rule_binder_err() { + assert!(Target1::validate(0).is_err()); + assert!(Target2::validate(0).is_err()); + assert!(Target3::validate(0).is_err()); + assert!(Target4::validate(0).is_err()); + assert!(Target5::validate(0).is_err()); + assert!(Target6::validate(0).is_err()); + assert!(Target7::validate(0).is_err()); + } +} diff --git a/src/rule/composer/xor.rs b/src/rule/composer/xor.rs new file mode 100644 index 0000000..3d99798 --- /dev/null +++ b/src/rule/composer/xor.rs @@ -0,0 +1,142 @@ +use crate::rule::composer::Not; +use crate::{And, Or}; + +/// This is a type that represents logical exclusive disjunction in logic. +/// # Example +/// ```rust +/// use refined_type::rule::composer::Xor; +/// use refined_type::rule::{Invalid, Rule, Valid};use refined_type::Xor; +/// +/// type ValidI8 = Valid; +/// type InvalidI8 = Invalid; +/// +/// type Target1 = Xor![ValidI8, ValidI8]; // 2: ERR +/// type Target2 = Xor![ValidI8, InvalidI8]; // 1: PASS +/// type Target3 = Xor![InvalidI8, ValidI8]; // 1: PASS +/// type Target4 = Xor![InvalidI8, InvalidI8]; // 0: ERR +/// +/// assert!(Target2::validate(0).is_ok()); +/// assert!(Target3::validate(0).is_ok()); +/// +/// assert!(Target1::validate(0).is_err()); +/// assert!(Target4::validate(0).is_err()); +/// ``` +pub type Xor = Or![And![RULE1, Not], And![Not, RULE2]]; + +#[macro_export] +macro_rules! Xor { + ($rule1:ty, $rule2:ty) => { + $crate::rule::composer::Xor<$rule1, $rule2> + }; + + ($rule1:ty, $($rule2: ty), +) => { + $crate::rule::composer::Xor<$rule1, $crate::Xor![$($rule2), +]> + } +} + +#[cfg(test)] +mod test_2 { + use crate::rule::{Invalid, Rule, Valid}; + + type ValidI8 = Valid; + type InvalidI8 = Invalid; + + type Target1 = Xor![ValidI8, ValidI8]; // 2: ERR + type Target2 = Xor![ValidI8, InvalidI8]; // 1: PASS + type Target3 = Xor![InvalidI8, ValidI8]; // 1: PASS + type Target4 = Xor![InvalidI8, InvalidI8]; // 0: ERR + + #[test] + fn test_rule_binder_ok() { + assert!(Target2::validate(0).is_ok()); + assert!(Target3::validate(0).is_ok()); + } + + #[test] + fn test_rule_binder_err() { + assert!(Target1::validate(0).is_err()); + assert!(Target4::validate(0).is_err()); + } +} + +#[cfg(test)] +mod test_3 { + use crate::rule::{Invalid, Rule, Valid}; + + type ValidI8 = Valid; + type InvalidI8 = Invalid; + + type Target1 = Xor![ValidI8, ValidI8, ValidI8]; // 3: PASS + type Target2 = Xor![ValidI8, ValidI8, InvalidI8]; // 2: ERR + type Target3 = Xor![ValidI8, InvalidI8, ValidI8]; // 2: ERR + type Target4 = Xor![ValidI8, InvalidI8, InvalidI8]; // 1: PASS + type Target5 = Xor![InvalidI8, ValidI8, ValidI8]; // 2: ERR + type Target6 = Xor![InvalidI8, ValidI8, InvalidI8]; // 1: PASS + type Target7 = Xor![InvalidI8, InvalidI8, ValidI8]; // 1: PASS + type Target8 = Xor![InvalidI8, InvalidI8, InvalidI8]; // 0: ERR + + #[test] + fn test_rule_binder_ok() { + assert!(Target1::validate(0).is_ok()); + assert!(Target4::validate(0).is_ok()); + assert!(Target6::validate(0).is_ok()); + assert!(Target7::validate(0).is_ok()); + } + + #[test] + fn test_rule_binder_err() { + assert!(Target2::validate(0).is_err()); + assert!(Target3::validate(0).is_err()); + assert!(Target5::validate(0).is_err()); + assert!(Target8::validate(0).is_err()); + } +} + +#[cfg(test)] +mod test_4 { + use crate::rule::{Invalid, Rule, Valid}; + + type ValidI8 = Valid; + type InvalidI8 = Invalid; + + type Target1 = Xor![ValidI8, ValidI8, ValidI8, ValidI8]; // 4: ERR + type Target2 = Xor![ValidI8, ValidI8, ValidI8, InvalidI8]; // 3: PASS + type Target3 = Xor![ValidI8, ValidI8, InvalidI8, ValidI8]; // 3: PASS + type Target4 = Xor![ValidI8, ValidI8, InvalidI8, InvalidI8]; // 2: ERR + type Target5 = Xor![ValidI8, InvalidI8, ValidI8, ValidI8]; // 3: PASS + type Target6 = Xor![ValidI8, InvalidI8, ValidI8, InvalidI8]; // 2: ERR + type Target7 = Xor![ValidI8, InvalidI8, InvalidI8, ValidI8]; // 2: ERR + type Target8 = Xor![ValidI8, InvalidI8, InvalidI8, InvalidI8]; // 1: PASS + type Target9 = Xor![InvalidI8, ValidI8, ValidI8, ValidI8]; // 3: PASS + type Target10 = Xor![InvalidI8, ValidI8, ValidI8, InvalidI8]; // 2: ERR + type Target11 = Xor![InvalidI8, ValidI8, InvalidI8, ValidI8]; // 2: ERR + type Target12 = Xor![InvalidI8, ValidI8, InvalidI8, InvalidI8]; // 1: PASS + type Target13 = Xor![InvalidI8, InvalidI8, ValidI8, ValidI8]; // 2: ERR + type Target14 = Xor![InvalidI8, InvalidI8, ValidI8, InvalidI8]; // 1: PASS + type Target15 = Xor![InvalidI8, InvalidI8, InvalidI8, ValidI8]; // 1: PASS + type Target16 = Xor![InvalidI8, InvalidI8, InvalidI8, InvalidI8]; // 0: ERR + + #[test] + fn test_rule_binder_ok() { + assert!(Target2::validate(0).is_ok()); + assert!(Target3::validate(0).is_ok()); + assert!(Target5::validate(0).is_ok()); + assert!(Target8::validate(0).is_ok()); + assert!(Target9::validate(0).is_ok()); + assert!(Target12::validate(0).is_ok()); + assert!(Target14::validate(0).is_ok()); + assert!(Target15::validate(0).is_ok()); + } + + #[test] + fn test_rule_binder_err() { + assert!(Target1::validate(0).is_err()); + assert!(Target4::validate(0).is_err()); + assert!(Target6::validate(0).is_err()); + assert!(Target7::validate(0).is_err()); + assert!(Target10::validate(0).is_err()); + assert!(Target11::validate(0).is_err()); + assert!(Target13::validate(0).is_err()); + assert!(Target16::validate(0).is_err()); + } +}