1use std::fmt;
2use std::sync::Arc;
3
4use crate::error::Result;
5
6pub trait Mod<Q> {
16 fn apply(self, q: &mut Q);
18}
19
20impl<Q> Mod<Q> for () {
22 fn apply(self, _q: &mut Q) {}
23}
24
25impl<Q, M: Mod<Q>> Mod<Q> for Option<M> {
28 fn apply(self, q: &mut Q) {
29 if let Some(m) = self {
30 m.apply(q);
31 }
32 }
33}
34
35impl<Q, M: Mod<Q>> Mod<Q> for Vec<M> {
36 fn apply(self, q: &mut Q) {
37 for m in self {
38 m.apply(q);
39 }
40 }
41}
42
43impl<Q, M: Mod<Q>, const N: usize> Mod<Q> for [M; N] {
44 fn apply(self, q: &mut Q) {
45 for m in self {
46 m.apply(q);
47 }
48 }
49}
50
51macro_rules! impl_mod_tuple {
52 ($($name:ident),+) => {
53 #[allow(non_snake_case)]
54 impl<Q, $($name: Mod<Q>),+> Mod<Q> for ($($name,)+) {
55 fn apply(self, q: &mut Q) {
56 let ($($name,)+) = self;
57 $($name.apply(q);)+
58 }
59 }
60 };
61}
62
63impl_mod_tuple!(A);
64impl_mod_tuple!(A, B);
65impl_mod_tuple!(A, B, C);
66impl_mod_tuple!(A, B, C, D);
67impl_mod_tuple!(A, B, C, D, E);
68impl_mod_tuple!(A, B, C, D, E, F);
69impl_mod_tuple!(A, B, C, D, E, F, G);
70impl_mod_tuple!(A, B, C, D, E, F, G, H);
71impl_mod_tuple!(A, B, C, D, E, F, G, H, I);
72impl_mod_tuple!(A, B, C, D, E, F, G, H, I, J);
73impl_mod_tuple!(A, B, C, D, E, F, G, H, I, J, K);
74impl_mod_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
75impl_mod_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
76impl_mod_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
77impl_mod_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
78impl_mod_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
79
80pub struct ModFn<F>(F);
86
87pub fn mod_fn<F>(f: F) -> ModFn<F> {
92 ModFn(f)
93}
94
95impl<F> fmt::Debug for ModFn<F> {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 f.write_str("ModFn")
98 }
99}
100
101impl<Q, F: FnOnce(&mut Q)> Mod<Q> for ModFn<F> {
102 fn apply(self, q: &mut Q) {
103 (self.0)(q);
104 }
105}
106
107pub trait BuildMod<Q>: fmt::Debug + Send + Sync {
119 fn apply(&self, q: &mut Q) -> Result<()>;
121}
122
123impl<Q, T: BuildMod<Q> + ?Sized> BuildMod<Q> for Arc<T> {
124 fn apply(&self, q: &mut Q) -> Result<()> {
125 (**self).apply(q)
126 }
127}
128
129impl<Q, T: BuildMod<Q> + ?Sized> BuildMod<Q> for Box<T> {
130 fn apply(&self, q: &mut Q) -> Result<()> {
131 (**self).apply(q)
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use crate::error::Error;
139
140 type Q = Vec<&'static str>;
142
143 struct Push(&'static str);
144
145 impl Mod<Q> for Push {
146 fn apply(self, q: &mut Q) {
147 q.push(self.0);
148 }
149 }
150
151 fn applied<M: Mod<Q>>(m: M) -> Q {
152 let mut q = Q::new();
153 m.apply(&mut q);
154 q
155 }
156
157 #[test]
158 fn unit_applies_nothing() {
159 assert_eq!(applied(()), Vec::<&str>::new());
160 }
161
162 #[test]
163 fn tuples_apply_left_to_right() {
164 assert_eq!(applied((Push("a"),)), vec!["a"]);
165 assert_eq!(applied((Push("a"), Push("b"))), vec!["a", "b"]);
166 assert_eq!(
167 applied((Push("a"), Push("b"), Push("c"), Push("d"))),
168 vec!["a", "b", "c", "d"]
169 );
170 }
171
172 #[test]
173 fn tuples_reach_arity_sixteen() {
174 let q = applied((
175 Push("1"),
176 Push("2"),
177 Push("3"),
178 Push("4"),
179 Push("5"),
180 Push("6"),
181 Push("7"),
182 Push("8"),
183 Push("9"),
184 Push("10"),
185 Push("11"),
186 Push("12"),
187 Push("13"),
188 Push("14"),
189 Push("15"),
190 Push("16"),
191 ));
192 assert_eq!(q.len(), 16);
193 assert_eq!(q.first(), Some(&"1"));
194 assert_eq!(q.last(), Some(&"16"));
195 }
196
197 #[test]
198 fn tuples_nest_so_arity_is_never_a_ceiling() {
199 assert_eq!(
200 applied((Push("a"), (Push("b"), (Push("c"), Push("d"))), Push("e"))),
201 vec!["a", "b", "c", "d", "e"]
202 );
203 }
204
205 #[test]
206 fn tuples_mix_mod_kinds() {
207 let q = applied((
208 Push("first"),
209 None::<Push>,
210 Some(Push("maybe")),
211 vec![Push("v1"), Push("v2")],
212 [Push("a1"), Push("a2")],
213 (),
214 mod_fn(|q: &mut Q| q.push("closure")),
215 ));
216 assert_eq!(q, vec!["first", "maybe", "v1", "v2", "a1", "a2", "closure"]);
217 }
218
219 #[allow(clippy::unnecessary_lazy_evaluations)]
222 #[test]
223 fn option_is_how_conditionals_are_written() {
224 let admin = false;
225 assert_eq!(applied((!admin).then(|| Push("scoped"))), vec!["scoped"]);
226 let admin = true;
227 assert_eq!(
228 applied((!admin).then(|| Push("scoped"))),
229 Vec::<&str>::new()
230 );
231 }
232
233 #[test]
234 fn nested_options_collapse() {
235 assert_eq!(applied(Some(Some(Push("deep")))), vec!["deep"]);
236 assert_eq!(applied(Some(None::<Push>)), Vec::<&str>::new());
237 }
238
239 #[test]
240 fn empty_collections_apply_nothing() {
241 assert_eq!(applied(Vec::<Push>::new()), Vec::<&str>::new());
242 assert_eq!(applied([] as [Push; 0]), Vec::<&str>::new());
243 }
244
245 type BoxedMod = Box<dyn FnOnce(&mut Q)>;
247
248 #[test]
249 fn a_vec_of_erased_mods_is_a_mod() {
250 let mods: Vec<BoxedMod> = vec![
253 Box::new(|q: &mut Q| q.push("one")),
254 Box::new(|q: &mut Q| q.push("two")),
255 ];
256 let mods: Vec<_> = mods.into_iter().map(mod_fn).collect();
257 assert_eq!(applied(mods), vec!["one", "two"]);
258 }
259
260 #[derive(Debug)]
261 struct AppendAtBuild(&'static str);
262
263 impl BuildMod<Q> for AppendAtBuild {
264 fn apply(&self, q: &mut Q) -> Result<()> {
265 q.push(self.0);
266 Ok(())
267 }
268 }
269
270 #[derive(Debug)]
271 struct Refuse;
272
273 impl BuildMod<Q> for Refuse {
274 fn apply(&self, _q: &mut Q) -> Result<()> {
275 Err(Error::Incomplete("a schema"))
276 }
277 }
278
279 #[test]
280 fn build_mods_run_repeatedly_and_can_fail() {
281 let mods: Vec<Arc<dyn BuildMod<Q>>> = vec![Arc::new(AppendAtBuild("once"))];
282
283 let mut first = Q::new();
284 let mut second = Q::new();
285 for m in &mods {
286 m.apply(&mut first).unwrap();
287 m.apply(&mut second).unwrap();
288 }
289 assert_eq!(first, vec!["once"]);
290 assert_eq!(
291 second,
292 vec!["once"],
293 "a build mod is not consumed by a build"
294 );
295
296 assert!(Refuse.apply(&mut first).is_err());
297 }
298}