Skip to main content

use_constraint/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4//! Lightweight named constraint 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 ConstraintEvaluation<'a> {
13    label: &'a str,
14    result: CheckResult,
15}
16
17impl<'a> ConstraintEvaluation<'a> {
18    #[must_use]
19    pub const fn label(&self) -> &'a str {
20        self.label
21    }
22
23    #[must_use]
24    pub const fn result(&self) -> CheckResult {
25        self.result
26    }
27
28    #[must_use]
29    pub const fn satisfied(&self) -> bool {
30        self.result.is_pass()
31    }
32}
33
34pub struct Constraint<T: ?Sized, F> {
35    label: &'static str,
36    evaluator: F,
37    marker: PhantomData<fn(&T)>,
38}
39
40impl<T: ?Sized, F> Constraint<T, F>
41where
42    F: Fn(&T) -> bool,
43{
44    #[must_use]
45    pub const fn new(label: &'static str, evaluator: F) -> Self {
46        Self {
47            label,
48            evaluator,
49            marker: PhantomData,
50        }
51    }
52
53    #[must_use]
54    pub const fn label(&self) -> &'static str {
55        self.label
56    }
57
58    #[must_use]
59    pub fn evaluate(&self, value: &T) -> ConstraintEvaluation<'static> {
60        ConstraintEvaluation {
61            label: self.label,
62            result: check((self.evaluator)(value)),
63        }
64    }
65
66    #[must_use]
67    pub fn is_satisfied_by(&self, value: &T) -> bool {
68        self.evaluate(value).satisfied()
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::Constraint;
75
76    #[test]
77    fn constraints_report_satisfaction() {
78        let constraint = Constraint::<str, _>::new("non-empty", |value| !value.is_empty());
79        let evaluation = constraint.evaluate("rustuse");
80
81        assert_eq!(evaluation.label(), "non-empty");
82        assert!(evaluation.satisfied());
83    }
84}