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, HashMap};
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
70impl<K: std::hash::Hash + Eq, T: ExtendBuilder> ExtendBuilder for HashMap<K, T> {
71 fn extend_from(&mut self, other: Self, strategy: ExtendStrategy) {
72 use std::collections::hash_map::Entry::*;
73 for (other_k, other_v) in other.into_iter() {
74 match self.entry(other_k) {
75 Vacant(vacant_entry) => {
76 vacant_entry.insert(other_v);
77 }
78 Occupied(mut occupied_entry) => {
79 occupied_entry.get_mut().extend_from(other_v, strategy);
80 }
81 }
82 }
83 }
84}
85
86define_derive_deftly! {
87 /// Provide an [`ExtendBuilder`] implementation for a struct's builder.
88 ///
89 /// This template is only sensible when used alongside `#[derive(Builder)]`.
90 ///
91 /// The provided `extend_from` function will behave as:
92 /// * For every non-`sub_builder` field,
93 /// if there is a value set in `other`,
94 /// replace the value in `self` (if any) with that value.
95 /// (Otherwise, leave the value in `self` as it is).
96 /// * For every `sub_builder` field,
97 /// recursively use `extend_from` to extend that builder
98 /// from the corresponding builder in `other`.
99 ///
100 /// # Interaction with `sub_builder`.
101 ///
102 /// When a field in the struct is tagged with `#[builder(sub_builder)]`,
103 /// you must also tag the same field with `#[deftly(extend_builder(sub_builder))]`;
104 /// otherwise, compilation will fail.
105 ///
106 /// # Interaction with `strip_option` and `default`.
107 ///
108 /// **The flags have no special effect on the `ExtendBuilder`, and will work fine.**
109 ///
110 /// (See comments in the code for details about why, and what this means.
111 /// Remember, `builder(default)` is applied when `build()` is called,
112 /// and does not automatically cause an un-set option to count as set.)
113 export ExtendBuilder for struct, expect items:
114
115 impl $crate::extend_builder::ExtendBuilder for $<$ttype Builder> {
116 fn extend_from(&mut self, other: Self, strategy: $crate::extend_builder::ExtendStrategy) {
117 let _ = strategy; // This will be unused when there is no sub-builder.
118 ${for fields {
119
120 ${if fmeta(extend_builder(sub_builder)) {
121 $crate::extend_builder::ExtendBuilder::extend_from(&mut self.$fname, other.$fname, strategy);
122 } else {
123 // Note that we do not need any special handling here for `strip_option` or
124 // `default`.
125 //
126 // Recall that:
127 // * `strip_option` only takes effect in a setter method,
128 // and causes the setter to wrap an additional Some() around its argument.
129 // * `default` takes effect in the build method,
130 // and controls that method's behavior when.
131 //
132 // In both cases, when the built object has a field of type `T`,
133 // the builder will have a corresponding field of type `Option<T>`,
134 // and will represent an un-set field with `None`.
135 // Therefore, since these flags don't effect the representation of a set or un-set field,
136 // our `extend_from` function doesn't need to know about them.
137 if let Some(other_val) = other.$fname {
138 self.$fname = Some(other_val);
139 }
140 }}
141
142 }}
143 }
144 }
145}
146
147/// Helper for `derive_deftly(TorConfig)`: implements ExtendBuilder for a field by replacing
148/// one value with another.
149///
150/// This is the default behavior for non-subbuilder fields.
151pub fn extend_with_replace<T>(cfg: &mut T, value: T, _strategy: ExtendStrategy) {
152 *cfg = value;
153}
154
155#[cfg(test)]
156mod test {
157 // @@ begin test lint list maintained by maint/add_warning @@
158 #![allow(clippy::bool_assert_comparison)]
159 #![allow(clippy::clone_on_copy)]
160 #![allow(clippy::dbg_macro)]
161 #![allow(clippy::mixed_attributes_style)]
162 #![allow(clippy::print_stderr)]
163 #![allow(clippy::print_stdout)]
164 #![allow(clippy::single_char_pattern)]
165 #![allow(clippy::unwrap_used)]
166 #![allow(clippy::unchecked_time_subtraction)]
167 #![allow(clippy::useless_vec)]
168 #![allow(clippy::needless_pass_by_value)]
169 #![allow(clippy::string_slice)] // See arti#2571
170 //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
171
172 use super::*;
173 use derive_deftly::Deftly;
174
175 #[derive(Clone, Debug, derive_builder::Builder, Eq, PartialEq, Deftly)]
176 #[derive_deftly(ExtendBuilder)]
177 struct Album {
178 title: String,
179 year: u32,
180 #[builder(setter(strip_option), default)]
181 n_volumes: Option<u8>,
182 #[builder(sub_builder)]
183 #[deftly(extend_builder(sub_builder))]
184 artist: Artist,
185 }
186
187 #[derive(Clone, Debug, derive_builder::Builder, Eq, PartialEq, Deftly)]
188 #[derive_deftly(ExtendBuilder)]
189 struct Artist {
190 name: String,
191 #[builder(setter(strip_option), default)]
192 year_formed: Option<u32>,
193 }
194
195 #[test]
196 fn extend() {
197 let mut a = AlbumBuilder::default();
198 a.artist().year_formed(1940);
199 a.title("Untitled".to_string());
200
201 let mut b = AlbumBuilder::default();
202 b.year(1980).artist().name("Unknown artist".to_string());
203 let mut aa = a.clone();
204 aa.extend_from(b, ExtendStrategy::ReplaceLists);
205 let aa = aa.build().unwrap();
206 assert_eq!(
207 aa,
208 Album {
209 title: "Untitled".to_string(),
210 year: 1980,
211 n_volumes: None,
212 artist: Artist {
213 name: "Unknown artist".to_string(),
214 year_formed: Some(1940)
215 }
216 }
217 );
218
219 let mut b = AlbumBuilder::default();
220 b.year(1969)
221 .title("Hot Rats".to_string())
222 .artist()
223 .name("Frank Zappa".into());
224 let mut aa = a.clone();
225 aa.extend_from(b, ExtendStrategy::ReplaceLists);
226 let aa = aa.build().unwrap();
227 assert_eq!(
228 aa,
229 Album {
230 title: "Hot Rats".to_string(),
231 year: 1969,
232 n_volumes: None,
233 artist: Artist {
234 name: "Frank Zappa".to_string(),
235 year_formed: Some(1940)
236 }
237 }
238 );
239 }
240
241 #[derive(Clone, Debug, derive_builder::Builder, Eq, PartialEq, Deftly)]
242 #[builder(derive(Debug, Eq, PartialEq))]
243 #[derive_deftly(ExtendBuilder)]
244 struct DAndS {
245 simple: Option<u32>,
246 #[builder(default = "Some(123)")]
247 dflt: Option<u32>,
248 #[builder(setter(strip_option))]
249 strip: Option<u32>,
250 #[builder(setter(strip_option), default = "Some(456)")]
251 strip_dflt: Option<u32>,
252 }
253 // For reference, the above will crate code something like the example below.
254 // (This may help the tests make more sense)
255 /*
256 #[derive(Default)]
257 struct DAndSBuilder {
258 simple: Option<Option<u32>>,
259 dflt: Option<Option<u32>>,
260 strip: Option<Option<u32>>,
261 strip_dflt: Option<Option<u32>>,
262 }
263 #[allow(unused)]
264 impl DAndSBuilder {
265 fn simple(&mut self, val: Option<u32>) -> &mut Self {
266 self.simple = Some(val);
267 self
268 }
269 fn dflt(&mut self, val: Option<u32>) -> &mut Self {
270 self.dflt = Some(val);
271 self
272 }
273 fn strip(&mut self, val: u32) -> &mut Self {
274 self.strip = Some(Some(val));
275 self
276 }
277 fn strip_dflt(&mut self, val: u32) -> &mut Self {
278 self.strip = Some(Some(val));
279 self
280 }
281 fn build(&self) -> Result<DAndS, DAndSBuilderError> {
282 Ok(DAndS {
283 simple: self
284 .simple
285 .ok_or(DAndSBuilderError::UninitializedField("simple"))?,
286 dflt: self.simple.unwrap_or(Some(123)),
287 strip: self
288 .strip
289 .ok_or(DAndSBuilderError::UninitializedField("strip"))?,
290 strip_dflt: self.simple.unwrap_or(Some(456)),
291 })
292 }
293 }
294 */
295
296 #[test]
297 // Demonstrate "default" and "strip_option" behavior without Extend.
298 fn default_and_strip_noextend() {
299 // Didn't set non-default options; this will fail.
300 assert!(DAndSBuilder::default().build().is_err());
301 assert!(DAndSBuilder::default().simple(Some(7)).build().is_err());
302 assert!(DAndSBuilder::default().strip(7).build().is_err());
303
304 // We can get away with setting only the non-defaulting options.
305 let v = DAndSBuilder::default()
306 .simple(Some(7))
307 .strip(77)
308 .build()
309 .unwrap();
310 assert_eq!(
311 v,
312 DAndS {
313 simple: Some(7),
314 dflt: Some(123),
315 strip: Some(77),
316 strip_dflt: Some(456)
317 }
318 );
319
320 // But we _can_ also set the defaulting options.
321 let v = DAndSBuilder::default()
322 .simple(Some(7))
323 .strip(77)
324 .dflt(Some(777))
325 .strip_dflt(7777)
326 .build()
327 .unwrap();
328 assert_eq!(
329 v,
330 DAndS {
331 simple: Some(7),
332 dflt: Some(777),
333 strip: Some(77),
334 strip_dflt: Some(7777)
335 }
336 );
337
338 // Now inspect the state of an uninitialized builder, and verify that it works as expected.
339 //
340 // Notably, everything is an Option<Option<...>> for this builder:
341 // `strip_option` only affects the behavior of the setter function,
342 // and `default` only affects the behavior of the build function.
343 // Neither affects the representation..
344 let mut bld = DAndSBuilder::default();
345 assert_eq!(
346 bld,
347 DAndSBuilder {
348 simple: None,
349 dflt: None,
350 strip: None,
351 strip_dflt: None
352 }
353 );
354 bld.simple(Some(7))
355 .strip(77)
356 .dflt(Some(777))
357 .strip_dflt(7777);
358 assert_eq!(
359 bld,
360 DAndSBuilder {
361 simple: Some(Some(7)),
362 dflt: Some(Some(777)),
363 strip: Some(Some(77)),
364 strip_dflt: Some(Some(7777)),
365 }
366 );
367 }
368
369 #[test]
370 fn default_and_strip_extending() {
371 fn combine_and_build(
372 b1: &DAndSBuilder,
373 b2: &DAndSBuilder,
374 ) -> Result<DAndS, DAndSBuilderError> {
375 let mut b = b1.clone();
376 b.extend_from(b2.clone(), ExtendStrategy::ReplaceLists);
377 b.build()
378 }
379
380 // We fail if neither builder sets some non-defaulting option.
381 let dflt_builder = DAndSBuilder::default();
382 assert!(combine_and_build(&dflt_builder, &dflt_builder).is_err());
383 let mut simple_only = DAndSBuilder::default();
384 simple_only.simple(Some(7));
385 let mut strip_only = DAndSBuilder::default();
386 strip_only.strip(77);
387 assert!(combine_and_build(&dflt_builder, &simple_only).is_err());
388 assert!(combine_and_build(&dflt_builder, &strip_only).is_err());
389 assert!(combine_and_build(&simple_only, &dflt_builder).is_err());
390 assert!(combine_and_build(&strip_only, &dflt_builder).is_err());
391 assert!(combine_and_build(&strip_only, &strip_only).is_err());
392 assert!(combine_and_build(&simple_only, &simple_only).is_err());
393
394 // But if every non-defaulting option is set in some builder, we succeed.
395 let v1 = combine_and_build(&strip_only, &simple_only).unwrap();
396 let v2 = combine_and_build(&simple_only, &strip_only).unwrap();
397 assert_eq!(v1, v2);
398 assert_eq!(
399 v1,
400 DAndS {
401 simple: Some(7),
402 dflt: Some(123),
403 strip: Some(77),
404 strip_dflt: Some(456)
405 }
406 );
407
408 // For every option, in every case: when a.extend(b) happens,
409 // a set option overrides a non-set option.
410 let mut all_set_1 = DAndSBuilder::default();
411 all_set_1
412 .simple(Some(1))
413 .strip(11)
414 .dflt(Some(111))
415 .strip_dflt(1111);
416 let v1 = combine_and_build(&all_set_1, &dflt_builder).unwrap();
417 let v2 = combine_and_build(&dflt_builder, &all_set_1).unwrap();
418 let expected_all_1s = DAndS {
419 simple: Some(1),
420 dflt: Some(111),
421 strip: Some(11),
422 strip_dflt: Some(1111),
423 };
424 assert_eq!(v1, expected_all_1s);
425 assert_eq!(v2, expected_all_1s);
426
427 // For every option, in every case: If the option is set in both cases,
428 // the extended-from option overrides the previous one.
429 let mut all_set_2 = DAndSBuilder::default();
430 all_set_2
431 .simple(Some(2))
432 .strip(22)
433 .dflt(Some(222))
434 .strip_dflt(2222);
435 let v1 = combine_and_build(&all_set_2, &all_set_1).unwrap();
436 let v2 = combine_and_build(&all_set_1, &all_set_2).unwrap();
437 let expected_all_2s = DAndS {
438 simple: Some(2),
439 dflt: Some(222),
440 strip: Some(22),
441 strip_dflt: Some(2222),
442 };
443 assert_eq!(v1, expected_all_1s); // since all_set_1 came last.
444 assert_eq!(v2, expected_all_2s); // since all_set_2 came last.
445 }
446}