Skip to main content

use_rule/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4//! Named reusable rule primitives.
5
6use core::marker::PhantomData;
7use use_check::{CheckResult, check};
8
9pub mod prelude;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct RuleEvaluation<'a> {
13    name: &'a str,
14    result: CheckResult,
15}
16
17impl<'a> RuleEvaluation<'a> {
18    #[must_use]
19    pub const fn name(&self) -> &'a str {
20        self.name
21    }
22
23    #[must_use]
24    pub const fn result(&self) -> CheckResult {
25        self.result
26    }
27
28    #[must_use]
29    pub const fn passed(&self) -> bool {
30        self.result.is_pass()
31    }
32}
33
34pub struct Rule<T: ?Sized, F> {
35    name: &'static str,
36    checker: F,
37    marker: PhantomData<fn(&T)>,
38}
39
40impl<T: ?Sized, F> Rule<T, F>
41where
42    F: Fn(&T) -> bool,
43{
44    #[must_use]
45    pub const fn new(name: &'static str, checker: F) -> Self {
46        Self {
47            name,
48            checker,
49            marker: PhantomData,
50        }
51    }
52
53    #[must_use]
54    pub const fn name(&self) -> &'static str {
55        self.name
56    }
57
58    #[must_use]
59    pub fn evaluate(&self, value: &T) -> RuleEvaluation<'static> {
60        RuleEvaluation {
61            name: self.name,
62            result: check((self.checker)(value)),
63        }
64    }
65
66    #[must_use]
67    pub fn passes(&self, value: &T) -> bool {
68        self.evaluate(value).passed()
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::Rule;
75
76    #[test]
77    fn rules_keep_a_name_and_result() {
78        let rule = Rule::<i32, _>::new("positive", |value| *value > 0);
79        let evaluation = rule.evaluate(&42);
80
81        assert_eq!(evaluation.name(), "positive");
82        assert!(evaluation.passed());
83    }
84}