diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 2bd1831..1c98113 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -11,3 +11,6 @@ pub use dispute_game::{Claim, GameStatus, GameType}; mod traits; pub use traits::{DisputeGame, DisputeSolver}; + +#[cfg(test)] +pub mod rule; diff --git a/crates/primitives/src/rule.rs b/crates/primitives/src/rule.rs new file mode 100644 index 0000000..7ed0e47 --- /dev/null +++ b/crates/primitives/src/rule.rs @@ -0,0 +1,84 @@ +//! This module contains the [Rule] type as well as several helper macros for applying +//! rules on top of one another. + +type Rule = Box anyhow::Result>; + +#[macro_export] +macro_rules! chain_rules { + ($state:expr, $($rule:expr),+) => {{ + let mut result = Ok($state); + + $( + result = match result { + Ok(val) => $rule(val), + err @ Err(_) => err, + }; + )+ + + result + }}; +} + +mod test { + use super::*; + + #[test] + fn apply_sequential_rules() { + let state = 5; + + let rule_lt_10: Rule = Box::new(|state: u32| { + if state < 10 { + Ok(state) + } else { + Err(anyhow::anyhow!("state must be less than 10")) + } + }); + let rule_double_10: Rule = Box::new(|state: u32| { + if state * 2 == 10 { + Ok(state) + } else { + Err(anyhow::anyhow!("state must be half of 10")) + } + }); + let rule_bitwise: Rule = Box::new(|state: u32| { + if state & 0xF == 0b0101 { + Ok(state) + } else { + Err(anyhow::anyhow!("state must be 5")) + } + }); + + let result = chain_rules!(state, rule_lt_10, rule_double_10, rule_bitwise); + assert!(result.is_ok()); + } + + #[test] + fn fail_sequential_rules() { + let state = 5; + + let rule_lt_10: Rule = Box::new(|state: u32| { + if state < 10 { + Ok(state) + } else { + Err(anyhow::anyhow!("state must be less than 10")) + } + }); + let rule_double_11: Rule = Box::new(|state: u32| { + if state * 2 == 11 { + Ok(state) + } else { + Err(anyhow::anyhow!("state must be half of 11")) + } + }); + let rule_bitwise: Rule = Box::new(|state: u32| { + if state & 0xF == 0b0101 { + Ok(state) + } else { + Err(anyhow::anyhow!("state must be 5")) + } + }); + + let result = chain_rules!(state, rule_lt_10, rule_double_11, rule_bitwise); + assert!(result.is_err()); + } +}