1use std::ops::Deref;
2
3pub trait Validation: Sized {
4 type Error;
5
6 fn is_valid(&self) -> Result<(), Self::Error>;
7
8 fn try_validate(self) -> Result<Valid<Self>, Self::Error> {
9 self.is_valid()?;
10 Ok(Valid(self))
11 }
12}
13
14pub trait CompositeValidation: Validation {
15 fn is_valid_composite(&self) -> Result<(), <Self as Validation>::Error>;
16}
17
18pub trait OptionsOf<C>: CompositeValidation {
19 fn set_options(&mut self, value: C);
20}
21
22impl<C> OptionsOf<C> for C
23where
24 C: CompositeValidation,
25{
26 fn set_options(&mut self, value: C) {
27 *self = value;
28 }
29}
30
31#[derive(Debug, Default, Clone, Eq, PartialEq)]
32pub struct Valid<T: Validation>(T);
33
34impl<T: Validation> Deref for Valid<T> {
35 type Target = T;
36
37 fn deref(&self) -> &Self::Target {
38 &self.0
39 }
40}
41
42impl<T: Validation> Valid<T> {
43 pub fn into_value(self) -> T {
44 self.0
45 }
46
47 pub fn set(self, set_fn: impl FnOnce(&mut T)) -> Result<Self, <T as Validation>::Error> {
48 let mut inner = self.0;
49 set_fn(&mut inner);
50 inner.try_validate()
51 }
52
53 pub fn with<F>(self, options: F) -> Result<Self, <T as Validation>::Error>
54 where
55 T: OptionsOf<F>,
56 F: Validation<Error = <T as Validation>::Error>,
57 {
58 let mut inner = self.0;
59 inner.set_options(options.try_validate()?.0);
60 inner.is_valid_composite()?;
61 Ok(Self(inner))
62 }
63
64 pub fn with2<F1, F2>(self, options1: F1, options2: F2) -> Result<Self, <T as Validation>::Error>
65 where
66 T: OptionsOf<F1> + OptionsOf<F2>,
67 F1: Validation<Error = <T as Validation>::Error>,
68 F2: Validation<Error = <T as Validation>::Error>,
69 {
70 let mut inner = self.0;
71 inner.set_options(options1.try_validate()?.0);
72 inner.set_options(options2.try_validate()?.0);
73 inner.is_valid_composite()?;
74 Ok(Self(inner))
75 }
76
77 pub fn with3<F1, F2, F3>(
78 self,
79 options1: F1,
80 options2: F2,
81 options3: F3,
82 ) -> Result<Self, <T as Validation>::Error>
83 where
84 T: OptionsOf<F1> + OptionsOf<F2> + OptionsOf<F3>,
85 F1: Validation<Error = <T as Validation>::Error>,
86 F2: Validation<Error = <T as Validation>::Error>,
87 F3: Validation<Error = <T as Validation>::Error>,
88 {
89 let mut inner = self.0;
90 inner.set_options(options1.try_validate()?.0);
91 inner.set_options(options2.try_validate()?.0);
92 inner.set_options(options3.try_validate()?.0);
93 inner.is_valid_composite()?;
94 Ok(Self(inner))
95 }
96}
97
98#[cfg(test)]
99mod test {
100 use super::*;
101
102 #[derive(Debug, Default, Clone, Eq, PartialEq)]
103 struct Test {
104 pub field: u32,
105 }
106
107 #[derive(Debug, Default, Clone, Eq, PartialEq)]
108 struct Test2 {
109 pub field: u32,
110 }
111
112 #[derive(Debug, Default, Clone, Eq, PartialEq)]
113 struct Test3 {
114 pub field: u32,
115 }
116
117 #[derive(Debug, Clone, Eq, PartialEq)]
118 enum TestError {
119 Test(Test),
120 Test2(Test2),
121 Test3(Test3),
122 TestParent(TestParent),
123 }
124
125 #[derive(Debug, Default, Clone, Eq, PartialEq)]
126 struct TestParent {
127 pub child: Test,
128 pub child2: Test2,
129 pub child3: Test3,
130 }
131
132 impl OptionsOf<Test> for TestParent {
133 fn set_options(&mut self, value: Test) {
134 self.child = value;
135 }
136 }
137
138 impl OptionsOf<Test2> for TestParent {
139 fn set_options(&mut self, value: Test2) {
140 self.child2 = value;
141 }
142 }
143
144 impl OptionsOf<Test3> for TestParent {
145 fn set_options(&mut self, value: Test3) {
146 self.child3 = value;
147 }
148 }
149
150 impl Validation for Test {
151 type Error = TestError;
152
153 fn is_valid(&self) -> Result<(), Self::Error> {
154 if self.field > 10 {
155 Ok(())
156 } else {
157 Err(TestError::Test(self.clone()))
158 }
159 }
160 }
161
162 impl Validation for Test2 {
163 type Error = TestError;
164
165 fn is_valid(&self) -> Result<(), Self::Error> {
166 if self.field > 20 {
167 Ok(())
168 } else {
169 Err(TestError::Test2(self.clone()))
170 }
171 }
172 }
173
174 impl Validation for Test3 {
175 type Error = TestError;
176
177 fn is_valid(&self) -> Result<(), Self::Error> {
178 if self.field > 20 {
179 Ok(())
180 } else {
181 Err(TestError::Test3(self.clone()))
182 }
183 }
184 }
185
186 impl CompositeValidation for TestParent {
187 fn is_valid_composite(&self) -> Result<(), Self::Error> {
188 if self.child.field == self.child2.field {
189 Ok(())
190 } else {
191 Err(TestError::TestParent(self.clone()))
192 }
193 }
194 }
195
196 impl Validation for TestParent {
197 type Error = TestError;
198
199 fn is_valid(&self) -> Result<(), Self::Error> {
200 self.child.is_valid()?;
201 self.child2.is_valid()?;
202 self.child3.is_valid()?;
203 self.is_valid_composite()
204 }
205 }
206
207 struct TestBuilder(Valid<TestParent>);
208 impl TestBuilder {
209 pub fn new() -> Self {
210 Self(Default::default())
211 }
212
213 pub fn with2<F1, F2>(
214 mut self,
215 options1: F1,
216 options2: F2,
217 ) -> Result<Self, <TestParent as Validation>::Error>
218 where
219 TestParent: OptionsOf<F1> + OptionsOf<F2>,
220 F1: Validation<Error = <TestParent as Validation>::Error>,
221 F2: Validation<Error = <TestParent as Validation>::Error>,
222 {
223 self.0 = self.0.with2(options1, options2)?;
224 Ok(self)
225 }
226
227 pub fn build(self) -> Valid<TestParent> {
228 self.0
229 }
230 }
231
232 #[test]
233 fn test_validation() {
234 let test = Test { field: 40 };
235 let test2 = Test2 { field: 40 };
236 let test3 = Test3 { field: 40 };
237 let parent: Valid<TestParent> = TestParent {
238 child: Test { field: 30 },
239 child2: Test2 { field: 30 },
240 child3: Test3 { field: 30 },
241 }
242 .try_validate()
243 .unwrap()
244 .set(|p| p.child3.field = 120)
245 .unwrap();
246
247 let parent = parent.with2(test.clone(), test2.clone()).unwrap();
248
249 let parent = parent.with3(test.clone(), test2, test3).unwrap();
250
251 assert_eq!(
252 parent.child.field, test.field,
253 "{} != {}",
254 parent.child.field, test.field
255 );
256
257 let fail = parent.with(Test { field: 1234 });
258 assert_eq!(
259 fail,
260 Err(TestError::TestParent(TestParent {
261 child: Test { field: 1234 },
262 child2: Test2 { field: 40 },
263 child3: Test3 { field: 40 }
264 }))
265 );
266
267 let parent = TestBuilder::new()
268 .with2(Test { field: 100 }, Test2 { field: 100 })
269 .unwrap()
270 .build();
271 assert_ne!(
272 parent.child.field, test.field,
273 "{} == {}",
274 parent.child.field, test.field
275 );
276 }
277}