1
//! Functionality for merging one config builder into another.
2

            
3
use derive_deftly::define_derive_deftly;
4
use std::collections::{BTreeMap, HashMap};
5

            
6
/// A builder that can be extended from another builder.
7
pub 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)]
45
pub 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

            
54
impl<K: Ord, T: ExtendBuilder> ExtendBuilder for BTreeMap<K, T> {
55
38
    fn extend_from(&mut self, other: Self, strategy: ExtendStrategy) {
56
        use std::collections::btree_map::Entry::*;
57
40
        for (other_k, other_v) in other.into_iter() {
58
34
            match self.entry(other_k) {
59
8
                Vacant(vacant_entry) => {
60
8
                    vacant_entry.insert(other_v);
61
8
                }
62
26
                Occupied(mut occupied_entry) => {
63
26
                    occupied_entry.get_mut().extend_from(other_v, strategy);
64
26
                }
65
            }
66
        }
67
38
    }
68
}
69

            
70
impl<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

            
86
define_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
34
        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.
151
pub fn extend_with_replace<T>(cfg: &mut T, value: T, _strategy: ExtendStrategy) {
152
    *cfg = value;
153
}
154

            
155
#[cfg(test)]
156
mod 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
}