1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use 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}