ddd_toolkit_core/specification.rs
1/// **English:**<br>
2/// Fallible predicate over a borrowed candidate, with consuming boolean combinators.
3/// `T` may be unsized, but specification values must be sized. Combinations evaluate left to right,
4/// short-circuit on a decisive boolean, and propagate the first error without evaluating later rules.
5/// Implementations may use interior state; purity and thread safety are not required.
6///
7/// Combine rules over an unsized string candidate and invert the result.
8///
9/// **日本語:**<br>
10/// 借用した候補に対する失敗し得る述語と、自身を消費する論理結合操作です。
11/// `T` はサイズ不定でもよいですが、仕様の値自体はサイズ確定が必要です。結合は左から評価し、
12/// 真偽が確定すると短絡し、エラーでは後続の規則を評価せず最初のエラーを伝播します。
13/// 実装は内部状態を利用でき、純粋性やスレッド安全性は要求しません。
14///
15/// サイズ不定の文字列候補に対する規則を結合し、結果を反転します。
16///
17/// # Example / 使用例
18///
19/// ```rust
20/// use ddd_toolkit_core::Specification;
21///
22/// struct NonEmpty;
23/// impl Specification<str> for NonEmpty {
24/// type Error = std::convert::Infallible;
25/// fn is_satisfied_by(&self, candidate: &str) -> Result<bool, Self::Error> {
26/// Ok(!candidate.is_empty())
27/// }
28/// }
29/// let rule = NonEmpty.and(NonEmpty).not();
30/// assert_eq!(rule.is_satisfied_by("order"), Ok(false));
31/// assert_eq!(rule.is_satisfied_by(""), Ok(true));
32/// ```
33pub trait Specification<T: ?Sized>: Sized {
34 /// **English:**<br>
35 /// Evaluation failure, distinct from a successfully evaluated `false` result.
36 ///
37 /// **日本語:**<br>
38 /// 正常に評価された `false` とは区別される、評価の失敗です。
39 type Error;
40
41 /// **English:**<br>
42 /// Returns whether `candidate` satisfies the rule or why evaluation failed.
43 /// The candidate remains borrowed; side effects and panic conditions depend on the rule.
44 ///
45 /// **日本語:**<br>
46 /// `candidate` が規則を満たすか、評価が失敗した理由を返します。
47 /// 候補は借用のままで、副作用とパニックの条件は規則に依存します。
48 fn is_satisfied_by(&self, candidate: &T) -> Result<bool, Self::Error>;
49
50 /// **English:**<br>
51 /// Consumes both rules to form [`And`], evaluating `other` only if this rule returns `Ok(true)`.
52 /// Both must share an error type. Construction evaluates neither rule.
53 ///
54 /// **日本語:**<br>
55 /// 両規則を消費して [`And`] を作り、この規則が `Ok(true)` の場合のみ `other` を評価します。
56 /// エラー型は共通でなければなりません。構築時にはどちらも評価しません。
57 fn and<S>(self, other: S) -> And<Self, S>
58 where
59 S: Specification<T, Error = Self::Error>,
60 {
61 And {
62 left: self,
63 right: other,
64 }
65 }
66
67 /// **English:**<br>
68 /// Consumes both rules to form [`Or`], evaluating `other` only if this rule returns `Ok(false)`.
69 /// An error from this rule is returned without consulting `other`.
70 ///
71 /// **日本語:**<br>
72 /// 両規則を消費して [`Or`] を作り、この規則が `Ok(false)` の場合のみ `other` を評価します。
73 /// この規則のエラーは `other` を評価せずに返します。
74 fn or<S>(self, other: S) -> Or<Self, S>
75 where
76 S: Specification<T, Error = Self::Error>,
77 {
78 Or {
79 left: self,
80 right: other,
81 }
82 }
83
84 /// **English:**<br>
85 /// Consumes this rule to form [`Not`], which flips successful booleans and preserves errors.
86 ///
87 /// **日本語:**<br>
88 /// この規則を消費して [`Not`] を作り、成功時の真偽を反転し、エラーは保持します。
89 fn not(self) -> Not<Self> {
90 Not { inner: self }
91 }
92}
93
94/// **English:**<br>
95/// Conjunction that evaluates the right rule only after the left returns `Ok(true)`.
96/// A left `Ok(false)` or error is returned immediately; a right result is forwarded unchanged.
97///
98/// **日本語:**<br>
99/// 左が `Ok(true)` を返した場合のみ右を評価する論理積です。
100/// 左の `Ok(false)` やエラーは直ちに返し、右を評価した場合はその結果を変更せず返します。
101pub struct And<L, R> {
102 left: L,
103 right: R,
104}
105
106impl<L, R> And<L, R> {
107 /// **English:**<br>
108 /// Stores `left` and `right` without evaluation; evaluation requires compatible specification impls.
109 ///
110 /// **日本語:**<br>
111 /// 評価せずに `left` と `right` を保持します。評価には互換な仕様の実装が必要です。
112 pub fn new(left: L, right: R) -> Self {
113 Self { left, right }
114 }
115}
116
117impl<T: ?Sized, L, R> Specification<T> for And<L, R>
118where
119 L: Specification<T>,
120 R: Specification<T, Error = L::Error>,
121{
122 type Error = L::Error;
123
124 fn is_satisfied_by(&self, candidate: &T) -> Result<bool, Self::Error> {
125 if !self.left.is_satisfied_by(candidate)? {
126 return Ok(false);
127 }
128 self.right.is_satisfied_by(candidate)
129 }
130}
131
132/// **English:**<br>
133/// Disjunction that evaluates the right rule only after the left returns `Ok(false)`.
134/// A left `Ok(true)` or error short-circuits; errors are not treated as a negative match.
135///
136/// **日本語:**<br>
137/// 左が `Ok(false)` を返した場合のみ右を評価する論理和です。
138/// 左の `Ok(true)` やエラーで短絡し、エラーを不一致として扱いません。
139pub struct Or<L, R> {
140 left: L,
141 right: R,
142}
143
144impl<L, R> Or<L, R> {
145 /// **English:**<br>
146 /// Owns the two operands in left-to-right evaluation order, without calling either rule.
147 ///
148 /// **日本語:**<br>
149 /// どちらも呼び出さず、左から右の評価順に二つの被演算子を所有します。
150 pub fn new(left: L, right: R) -> Self {
151 Self { left, right }
152 }
153}
154
155impl<T: ?Sized, L, R> Specification<T> for Or<L, R>
156where
157 L: Specification<T>,
158 R: Specification<T, Error = L::Error>,
159{
160 type Error = L::Error;
161
162 fn is_satisfied_by(&self, candidate: &T) -> Result<bool, Self::Error> {
163 if self.left.is_satisfied_by(candidate)? {
164 return Ok(true);
165 }
166 self.right.is_satisfied_by(candidate)
167 }
168}
169
170/// **English:**<br>
171/// Negation that evaluates its inner rule once, flips `Ok(bool)`, and propagates `Err` unchanged.
172///
173/// **日本語:**<br>
174/// 内側の規則を一度評価し、`Ok(bool)` を反転し、`Err` は変更せず伝播する否定です。
175pub struct Not<S> {
176 inner: S,
177}
178
179impl<S> Not<S> {
180 /// **English:**<br>
181 /// Wraps `inner` by value without evaluating or validating it.
182 ///
183 /// **日本語:**<br>
184 /// 評価や検証をせず、`inner` を値として包みます。
185 pub fn new(inner: S) -> Self {
186 Self { inner }
187 }
188}
189
190impl<T: ?Sized, S> Specification<T> for Not<S>
191where
192 S: Specification<T>,
193{
194 type Error = S::Error;
195
196 fn is_satisfied_by(&self, candidate: &T) -> Result<bool, Self::Error> {
197 self.inner.is_satisfied_by(candidate).map(|value| !value)
198 }
199}