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

            
3
use derive_deftly::define_derive_deftly;
4
use std::collections::BTreeMap;
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
50
    fn extend_from(&mut self, other: Self, strategy: ExtendStrategy) {
56
        use std::collections::btree_map::Entry::*;
57
52
        for (other_k, other_v) in other.into_iter() {
58
42
            match self.entry(other_k) {
59
8
                Vacant(vacant_entry) => {
60
8
                    vacant_entry.insert(other_v);
61
8
                }
62
34
                Occupied(mut occupied_entry) => {
63
34
                    occupied_entry.get_mut().extend_from(other_v, strategy);
64
34
                }
65
            }
66
        }
67
50
    }
68
}
69

            
70
define_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
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)]
132
mod 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
}