Skip to main content

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}