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}