tor_config/extend_builder.rs
1//! Functionality for merging one config builder into another.
2
3use derive_deftly::define_derive_deftly;
4use std::collections::BTreeMap;
5
6/// A builder that can be extended from another builder.
7pub trait ExtendBuilder {
8 /// Consume `other`, and merge its contents into `self`.
9 ///
10 /// Generally, whenever a field is set in `other`,
11 /// it should replace any corresponding field in `self`.
12 /// Unset fields in `other` should have no effect.
13 ///
14 /// We use this trait to implement map-style configuration options
15 /// that need to have defaults.
16 /// Rather than simply replacing the maps wholesale
17 /// (as would happen with serde defaults ordinarily)
18 /// we use this trait to copy inner options from the provided options over the defaults
19 /// in the most fine-grained manner possible.
20 ///
21 /// ## When `strategy` is [`ExtendStrategy::ReplaceLists`]:
22 ///
23 /// (No other strategies currently exist.)
24 ///
25 /// Every simple option that is set in `other` should be moved into `self`,
26 /// replacing a previous value (if there was one).
27 ///
28 /// Every list option that is set in `other` should be moved into `self`,
29 /// replacing a previous value (if there was one).
30 ///
31 /// Any complex option (one with an internal tree structure) that is set in `other`
32 /// should recursively be extended, replacing each piece of it that is set in other.
33 fn extend_from(&mut self, other: Self, strategy: ExtendStrategy);
34}
35
36/// Strategy for extending one builder with another.
37///
38/// Currently, only one strategy is defined:
39/// this enum exists so that we can define others in the future.
40#[derive(Clone, Debug, Copy, Eq, PartialEq)]
41// We declare this to be an exhaustive enum, since every ExtendBuilder implementation
42// must support every strategy.
43// So if we add a new strategy, that has to be a breaking change in `ExtendBuilder`.
44#[allow(clippy::exhaustive_enums)]
45pub enum ExtendStrategy {
46 /// Replace all simple options (those with no internal structure).
47 ///
48 /// Replace all list options.
49 ///
50 /// Recursively extend all tree options.
51 ReplaceLists,
52}
53
54impl<K: Ord, T: ExtendBuilder> ExtendBuilder for BTreeMap<K, T> {
55 fn extend_from(&mut self, other: Self, strategy: ExtendStrategy) {
56 use std::collections::btree_map::Entry::*;
57 for (other_k, other_v) in other.into_iter() {
58 match self.entry(other_k) {
59 Vacant(vacant_entry) => {
60 vacant_entry.insert(other_v);
61 }
62 Occupied(mut occupied_entry) => {
63 occupied_entry.get_mut().extend_from(other_v, strategy);
64 }
65 }
66 }
67 }
68}
69
70define_derive_deftly! {
71 /// Provide an [`ExtendBuilder`] implementation for a struct's builder.
72 ///
73 /// This template is only sensible when used alongside `#[derive(Builder)]`.
74 ///
75 /// The provided `extend_from` function will behave as:
76 /// * For every non-`sub_builder` field,
77 /// if there is a value set in `other`,
78 /// replace the value in `self` (if any) with that value.
79 /// (Otherwise, leave the value in `self` as it is).
80 /// * For every `sub_builder` field,
81 /// recursively use `extend_from` to extend that builder
82 /// from the corresponding builder in `other`.
83 ///
84 /// # Interaction with `sub_builder`.
85 ///
86 /// When a field in the struct is tagged with `#[builder(sub_builder)]`,
87 /// you must also tag the same field with `#[deftly(extend_builder(sub_builder))]`;
88 /// otherwise, compilation will fail.
89 ///
90 /// # Interaction with `strip_option` and `default`.
91 ///
92 /// **The flags have no special effect on the `ExtendBuilder`, and will work fine.**
93 ///
94 /// (See comments in the code for details about why, and what this means.
95 /// Remember, `builder(default)` is applied when `build()` is called,
96 /// and does not automatically cause an un-set option to count as set.)
97 export ExtendBuilder for struct, expect items:
98
99 impl $crate::extend_builder::ExtendBuilder for $<$ttype Builder> {
100 fn extend_from(&mut self, other: Self, strategy: $crate::extend_builder::ExtendStrategy) {
101 let _ = strategy; // This will be unused when there is no sub-builder.
102 ${for fields {
103
104 ${if fmeta(extend_builder(sub_builder)) {
105 $crate::extend_builder::ExtendBuilder::extend_from(&mut self.$fname, other.$fname, strategy);
106 } else {
107 // Note that we do not need any special handling here for `strip_option` or
108 // `default`.
109 //
110 // Recall that:
111 // * `strip_option` only takes effect in a setter method,
112 // and causes the setter to wrap an additional Some() around its argument.
113 // * `default` takes effect in the build method,
114 // and controls that method's behavior when.
115 //
116 // In both cases, when the built object has a field of type `T`,
117 // the builder will have a corresponding field of type `Option<T>`,
118 // and will represent an un-set field with `None`.
119 // Therefore, since these flags don't effect the representation of a set or un-set field,
120 // our `extend_from` function doesn't need to know about them.
121 if let Some(other_val) = other.$fname {
122 self.$fname = Some(other_val);
123 }
124 }}
125
126 }}
127 }
128 }
129}
130
131#[cfg(test)]
132mod test {
133 // @@ begin test lint list maintained by maint/add_warning @@
134 #![allow(clippy::bool_assert_comparison)]
135 #![allow(clippy::clone_on_copy)]
136 #![allow(clippy::dbg_macro)]
137 #![allow(clippy::mixed_attributes_style)]
138 #![allow(clippy::print_stderr)]
139 #![allow(clippy::print_stdout)]
140 #![allow(clippy::single_char_pattern)]
141 #![allow(clippy::unwrap_used)]
142 #![allow(clippy::unchecked_duration_subtraction)]
143 #![allow(clippy::useless_vec)]
144 #![allow(clippy::needless_pass_by_value)]
145 //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
146
147 use super::*;
148 use derive_deftly::Deftly;
149
150 #[derive(Clone, Debug, derive_builder::Builder, Eq, PartialEq, Deftly)]
151 #[derive_deftly(ExtendBuilder)]
152 struct Album {
153 title: String,
154 year: u32,
155 #[builder(setter(strip_option), default)]
156 n_volumes: Option<u8>,
157 #[builder(sub_builder)]
158 #[deftly(extend_builder(sub_builder))]
159 artist: Artist,
160 }
161
162 #[derive(Clone, Debug, derive_builder::Builder, Eq, PartialEq, Deftly)]
163 #[derive_deftly(ExtendBuilder)]
164 struct Artist {
165 name: String,
166 #[builder(setter(strip_option), default)]
167 year_formed: Option<u32>,
168 }
169
170 #[test]
171 fn extend() {
172 let mut a = AlbumBuilder::default();
173 a.artist().year_formed(1940);
174 a.title("Untitled".to_string());
175
176 let mut b = AlbumBuilder::default();
177 b.year(1980).artist().name("Unknown artist".to_string());
178 let mut aa = a.clone();
179 aa.extend_from(b, ExtendStrategy::ReplaceLists);
180 let aa = aa.build().unwrap();
181 assert_eq!(
182 aa,
183 Album {
184 title: "Untitled".to_string(),
185 year: 1980,
186 n_volumes: None,
187 artist: Artist {
188 name: "Unknown artist".to_string(),
189 year_formed: Some(1940)
190 }
191 }
192 );
193
194 let mut b = AlbumBuilder::default();
195 b.year(1969)
196 .title("Hot Rats".to_string())
197 .artist()
198 .name("Frank Zappa".into());
199 let mut aa = a.clone();
200 aa.extend_from(b, ExtendStrategy::ReplaceLists);
201 let aa = aa.build().unwrap();
202 assert_eq!(
203 aa,
204 Album {
205 title: "Hot Rats".to_string(),
206 year: 1969,
207 n_volumes: None,
208 artist: Artist {
209 name: "Frank Zappa".to_string(),
210 year_formed: Some(1940)
211 }
212 }
213 );
214 }
215
216 #[derive(Clone, Debug, derive_builder::Builder, Eq, PartialEq, Deftly)]
217 #[builder(derive(Debug, Eq, PartialEq))]
218 #[derive_deftly(ExtendBuilder)]
219 struct DAndS {
220 simple: Option<u32>,
221 #[builder(default = "Some(123)")]
222 dflt: Option<u32>,
223 #[builder(setter(strip_option))]
224 strip: Option<u32>,
225 #[builder(setter(strip_option), default = "Some(456)")]
226 strip_dflt: Option<u32>,
227 }
228 // For reference, the above will crate code something like the example below.
229 // (This may help the tests make more sense)
230 /*
231 #[derive(Default)]
232 struct DAndSBuilder {
233 simple: Option<Option<u32>>,
234 dflt: Option<Option<u32>>,
235 strip: Option<Option<u32>>,
236 strip_dflt: Option<Option<u32>>,
237 }
238 #[allow(unused)]
239 impl DAndSBuilder {
240 fn simple(&mut self, val: Option<u32>) -> &mut Self {
241 self.simple = Some(val);
242 self
243 }
244 fn dflt(&mut self, val: Option<u32>) -> &mut Self {
245 self.dflt = Some(val);
246 self
247 }
248 fn strip(&mut self, val: u32) -> &mut Self {
249 self.strip = Some(Some(val));
250 self
251 }
252 fn strip_dflt(&mut self, val: u32) -> &mut Self {
253 self.strip = Some(Some(val));
254 self
255 }
256 fn build(&self) -> Result<DAndS, DAndSBuilderError> {
257 Ok(DAndS {
258 simple: self
259 .simple
260 .ok_or(DAndSBuilderError::UninitializedField("simple"))?,
261 dflt: self.simple.unwrap_or(Some(123)),
262 strip: self
263 .strip
264 .ok_or(DAndSBuilderError::UninitializedField("strip"))?,
265 strip_dflt: self.simple.unwrap_or(Some(456)),
266 })
267 }
268 }
269 */
270
271 #[test]
272 // Demonstrate "default" and "strip_option" behavior without Extend.
273 fn default_and_strip_noextend() {
274 // Didn't set non-default options; this will fail.
275 assert!(DAndSBuilder::default().build().is_err());
276 assert!(DAndSBuilder::default().simple(Some(7)).build().is_err());
277 assert!(DAndSBuilder::default().strip(7).build().is_err());
278
279 // We can get away with setting only the non-defaulting options.
280 let v = DAndSBuilder::default()
281 .simple(Some(7))
282 .strip(77)
283 .build()
284 .unwrap();
285 assert_eq!(
286 v,
287 DAndS {
288 simple: Some(7),
289 dflt: Some(123),
290 strip: Some(77),
291 strip_dflt: Some(456)
292 }
293 );
294
295 // But we _can_ also set the defaulting options.
296 let v = DAndSBuilder::default()
297 .simple(Some(7))
298 .strip(77)
299 .dflt(Some(777))
300 .strip_dflt(7777)
301 .build()
302 .unwrap();
303 assert_eq!(
304 v,
305 DAndS {
306 simple: Some(7),
307 dflt: Some(777),
308 strip: Some(77),
309 strip_dflt: Some(7777)
310 }
311 );
312
313 // Now inspect the state of an uninitialized builder, and verify that it works as expected.
314 //
315 // Notably, everything is an Option<Option<...>> for this builder:
316 // `strip_option` only affects the behavior of the setter function,
317 // and `default` only affects the behavior of the build function.
318 // Neither affects the representation..
319 let mut bld = DAndSBuilder::default();
320 assert_eq!(
321 bld,
322 DAndSBuilder {
323 simple: None,
324 dflt: None,
325 strip: None,
326 strip_dflt: None
327 }
328 );
329 bld.simple(Some(7))
330 .strip(77)
331 .dflt(Some(777))
332 .strip_dflt(7777);
333 assert_eq!(
334 bld,
335 DAndSBuilder {
336 simple: Some(Some(7)),
337 dflt: Some(Some(777)),
338 strip: Some(Some(77)),
339 strip_dflt: Some(Some(7777)),
340 }
341 );
342 }
343
344 #[test]
345 fn default_and_strip_extending() {
346 fn combine_and_build(
347 b1: &DAndSBuilder,
348 b2: &DAndSBuilder,
349 ) -> Result<DAndS, DAndSBuilderError> {
350 let mut b = b1.clone();
351 b.extend_from(b2.clone(), ExtendStrategy::ReplaceLists);
352 b.build()
353 }
354
355 // We fail if neither builder sets some non-defaulting option.
356 let dflt_builder = DAndSBuilder::default();
357 assert!(combine_and_build(&dflt_builder, &dflt_builder).is_err());
358 let mut simple_only = DAndSBuilder::default();
359 simple_only.simple(Some(7));
360 let mut strip_only = DAndSBuilder::default();
361 strip_only.strip(77);
362 assert!(combine_and_build(&dflt_builder, &simple_only).is_err());
363 assert!(combine_and_build(&dflt_builder, &strip_only).is_err());
364 assert!(combine_and_build(&simple_only, &dflt_builder).is_err());
365 assert!(combine_and_build(&strip_only, &dflt_builder).is_err());
366 assert!(combine_and_build(&strip_only, &strip_only).is_err());
367 assert!(combine_and_build(&simple_only, &simple_only).is_err());
368
369 // But if every non-defaulting option is set in some builder, we succeed.
370 let v1 = combine_and_build(&strip_only, &simple_only).unwrap();
371 let v2 = combine_and_build(&simple_only, &strip_only).unwrap();
372 assert_eq!(v1, v2);
373 assert_eq!(
374 v1,
375 DAndS {
376 simple: Some(7),
377 dflt: Some(123),
378 strip: Some(77),
379 strip_dflt: Some(456)
380 }
381 );
382
383 // For every option, in every case: when a.extend(b) happens,
384 // a set option overrides a non-set option.
385 let mut all_set_1 = DAndSBuilder::default();
386 all_set_1
387 .simple(Some(1))
388 .strip(11)
389 .dflt(Some(111))
390 .strip_dflt(1111);
391 let v1 = combine_and_build(&all_set_1, &dflt_builder).unwrap();
392 let v2 = combine_and_build(&dflt_builder, &all_set_1).unwrap();
393 let expected_all_1s = DAndS {
394 simple: Some(1),
395 dflt: Some(111),
396 strip: Some(11),
397 strip_dflt: Some(1111),
398 };
399 assert_eq!(v1, expected_all_1s);
400 assert_eq!(v2, expected_all_1s);
401
402 // For every option, in every case: If the option is set in both cases,
403 // the extended-from option overrides the previous one.
404 let mut all_set_2 = DAndSBuilder::default();
405 all_set_2
406 .simple(Some(2))
407 .strip(22)
408 .dflt(Some(222))
409 .strip_dflt(2222);
410 let v1 = combine_and_build(&all_set_2, &all_set_1).unwrap();
411 let v2 = combine_and_build(&all_set_1, &all_set_2).unwrap();
412 let expected_all_2s = DAndS {
413 simple: Some(2),
414 dflt: Some(222),
415 strip: Some(22),
416 strip_dflt: Some(2222),
417 };
418 assert_eq!(v1, expected_all_1s); // since all_set_1 came last.
419 assert_eq!(v2, expected_all_2s); // since all_set_2 came last.
420 }
421}