1
//! Lists in builders
2
//!
3
//! Use [`define_list_builder_helper`] and [`define_list_builder_accessors`] together when
4
//! a configuration (or other struct with a builder)
5
//! wants to contain a `Vec` of config sub-entries.
6
//!
7
//! ### How to use these macros
8
//!
9
//!  * For each kind of list, define a `ThingList` type alias for the validated form,
10
//!    and call [`define_list_builder_helper`] to define a `ThingListBuilder` helper
11
//!    type.  (Different lists with the same Rust type, but which ought to have a different
12
//!    default, are different "kinds" and should each have a separately named type alias.)
13
//!
14
//!    (Or, alternatively, with a hand-written builder type, make the builder field be
15
//!    `Option<Vec<ElementBuilder>>`.)
16
//!
17
// An alternative design would be declare the field on `Outer` as `Vec<Thing>`, and to provide
18
// a `VecBuilder`.  But:
19
//
20
//  (i) the `.build()` method would have to be from a trait (because it would be `VecBuilder<Item>`
21
//  which would have to contain some `ItemBuilder`, and for the benefit of `VecBuilder::build()`).
22
//  Although derive_builder` does not provide that trait now, this problem is not insuperable,
23
//  but it would mean us inventing a `Buildable` trait and a macro to generate it, or forking
24
//  derive_builder further.
25
//
26
//  (ii) `VecBuilder<Item>::build()` would have to have the same default list for every
27
//  type Item (an empty list).  So places where the default list is not empty would need special
28
//  handling.  The special handling would look quite like what we have here.
29
//
30
//!  * For each struct field containing a list, in a struct deriving `Builder`,
31
//!    decorate the field with `#[builder(sub_builder, setter(custom))]`
32
//!    to (i) get `derive_builder` call the appropriate build method,
33
//!    (ii) suppress the `derive_builder`-generated setter.
34
//!
35
// `ThingListBuilder` exists for two reasons:
36
//
37
//  * derive_builder wants to call simply `build` on the builder struct field, and will
38
//    generate code for attaching the field name to any error which occurs.  We could
39
//    override the per-field build expression, but it would be quite a lot of typing and
40
//    would recapitulate the field name three times.
41
//
42
//  * The field accessors (which must be generated by a different macro_rules macros, at least
43
//    unless we soup up derive_builder some more) might need to do defaulting, too.  if
44
//    the builder field is its own type, that can be a method on that type.
45
//
46
//!  * For each struct containing lists, call [`define_list_builder_accessors`]
47
//!    to define the accessor methods.
48
//!
49
//! ### Example - list of structs with builders
50
//!
51
//! ```
52
//! use derive_builder::Builder;
53
//! use serde::{Deserialize, Serialize};
54
//! use tor_config::{define_list_builder_helper, define_list_builder_accessors, ConfigBuildError};
55
//!
56
//! #[derive(Builder, Debug, Eq, PartialEq)]
57
//! #[builder(build_fn(error = "ConfigBuildError"))]
58
//! #[builder(derive(Debug, Serialize, Deserialize))]
59
//! pub struct Thing { value: i32 }
60
//!
61
//! #[derive(Builder, Debug, Eq, PartialEq)]
62
//! #[builder(build_fn(error = "ConfigBuildError"))]
63
//! #[builder(derive(Debug, Serialize, Deserialize))]
64
//! pub struct Outer {
65
//!     /// List of things, being built as part of the configuration
66
//!     #[builder(sub_builder, setter(custom))]
67
//!     things: ThingList,
68
//! }
69
//!
70
//! define_list_builder_accessors! {
71
//!     struct OuterBuilder {
72
//!         pub things: [ThingBuilder],
73
//!     }
74
//! }
75
//!
76
//! /// Type alias for use by list builder macrology
77
//! type ThingList = Vec<Thing>;
78
//!
79
//! define_list_builder_helper! {
80
//!     pub(crate) struct ThingListBuilder {
81
//!         pub(crate) things: [ThingBuilder],
82
//!     }
83
//!     built: ThingList = things;
84
//!     default = vec![];
85
//! }
86
//!
87
//! let mut builder = OuterBuilder::default();
88
//! builder.things().push(ThingBuilder::default().value(42).clone());
89
//! assert_eq!{ builder.build().unwrap().things, &[Thing { value: 42 }] }
90
//!
91
//! builder.set_things(vec![ThingBuilder::default().value(38).clone()]);
92
//! assert_eq!{ builder.build().unwrap().things, &[Thing { value: 38 }] }
93
//! ```
94
//!
95
//! ### Example - list of trivial values
96
//!
97
//! ```
98
//! use derive_builder::Builder;
99
//! use serde::{Deserialize, Serialize};
100
//! use tor_config::{define_list_builder_helper, define_list_builder_accessors, ConfigBuildError};
101
//!
102
//! #[derive(Builder, Debug, Eq, PartialEq)]
103
//! #[builder(build_fn(error = "ConfigBuildError"))]
104
//! #[builder(derive(Debug, Serialize, Deserialize))]
105
//! pub struct Outer {
106
//!     /// List of values, being built as part of the configuration
107
//!     #[builder(sub_builder, setter(custom))]
108
//!     values: ValueList,
109
//! }
110
//!
111
//! define_list_builder_accessors! {
112
//!    struct OuterBuilder {
113
//!        pub values: [u32],
114
//!    }
115
//! }
116
//!
117
//! /// Type alias for use by list builder macrology
118
//! pub type ValueList = Vec<u32>;
119
//!
120
//! define_list_builder_helper! {
121
//!    pub(crate) struct ValueListBuilder {
122
//!        pub(crate) values: [u32],
123
//!    }
124
//!    built: ValueList = values;
125
//!    default = vec![27];
126
//!    item_build: |&value| Ok(value);
127
//! }
128
//!
129
//! let mut builder = OuterBuilder::default();
130
//! assert_eq!{ builder.build().unwrap().values, &[27] }
131
//!
132
//! builder.values().push(12);
133
//! assert_eq!{ builder.build().unwrap().values, &[27, 12] }
134
//! ```
135

            
136
use std::fmt;
137
use std::marker::PhantomData;
138
use std::str::FromStr;
139

            
140
use educe::Educe;
141
use itertools::Itertools;
142
use serde::{Deserialize, Deserializer, Serialize};
143
use thiserror::Error;
144

            
145
pub use crate::define_list_builder_accessors;
146
pub use crate::define_list_builder_helper;
147

            
148
/// Define a list builder struct for use with [`define_list_builder_accessors`]
149
///
150
/// Generates an builder struct that can be used with derive_builder
151
/// and [`define_list_builder_accessors`] to configure a list of some kind.
152
///
153
/// **See the [`list_builder` module documentation](crate::list_builder) for an overview.**
154
///
155
/// ### Generated struct
156
///
157
/// This macro-generated builder struct contains `Option<Vec<ThingBuilder>>`, to allow it to
158
/// distinguish "never set" from "has been adjusted or set, possibly to the empty list".
159
///
160
/// This struct is not exposed as part of the API for setting the configuration.
161
/// Generally the visibility (`$vis`) should be private,
162
/// but sometimes `pub(crate)` or `pub` is necessary,
163
/// for example if the list is to be included in a struct in another module or crate.
164
/// Usually `$field_vis` should be the same as `$vis`.
165
///
166
/// `#[derive(Default, Clone, Debug, Serialize, Deserialize)]`
167
///  will be applied to the generated builder,
168
/// but you can specify other attributes too.
169
/// There is no need to supply any documentation; this is an internal struct and
170
/// the macro will supply a suitable (bland) doc comment.
171
/// (If you do supply documentation, the autogenerated docs will be appended,
172
/// so start with a summary line.)
173
/// Documentation for the semantics and default value should be applied
174
/// to the field(s) in the containing struct(s).
175
///
176
/// `#[serde(transparent)]` will be applied to the generated `ThingBuilder` struct,
177
/// so that it deserializes just like `Option<Vec<Thing>>`.
178
///
179
/// ### Input to the macro
180
///
181
/// For the input syntax, refer to the docs autogenerated from the macro's matcher.
182
///
183
/// The `built` clause specifies the type of the built value, and how to construct it.
184
/// In the expression part, `things` (the field name) will be the default-resolved `Vec<Thing>`;
185
/// it should be consumed by the expression.
186
/// If the built value is simply a `Vec`, you can just write `built: ThingList = things;`.
187
///
188
/// The `default` clause must provide an expression evaluating to a `Vec<ThingBuilder>`.
189
///
190
/// The `item_build` clause, if supplied, provides a closure with type
191
/// `FnMut(&ThingBuilder) -> Result<Thing, ConfigBuildError>`;
192
/// the default is to call `thing_builder.build()`.
193
///
194
/// The `#[ serde $serde_attrs:tt ]`, if supplied, replace the serde attribute
195
/// `#[serde(transparent)]`.
196
/// The transparent attribute is applied by default
197
/// to arrange that the serde view of the list is precisely `Option<Vec>`.
198
/// If serialisation is done another way, for example with `#[serde(into)]`,
199
/// that must be specified here.
200
///
201
/// `[$generics]` are generics for `$ListBuilder`.
202
/// Inline bounds (`T: Debug`) are not supported; use a `where` clause instead.
203
/// Due to limitations of `macro_rules`, the parameters must be within `[ ]` rather than `< >`,
204
/// and an extraneous pair of `[ ]` must appear around any `$where_clauses`.
205
//
206
// This difficulty with macro_rules is not well documented.
207
// The upstream Rust bug tracker has this issue
208
//   https://github.com/rust-lang/rust/issues/73174
209
//   Matching function signature is nearly impossible in declarative macros (mbe)
210
// which is not precisely this problem but is very nearby.
211
// There's also the vapourware "declarative macros 2.0"
212
//   https://github.com/rust-lang/rust/issues/39412
213
#[macro_export]
214
macro_rules! define_list_builder_helper {
215
    {
216
        $(#[ $docs_and_attrs:meta ])*
217
        $vis:vis
218
        struct $ListBuilder:ident $( [ $($generics:tt)* ] )?
219
        $( where [ $($where_clauses:tt)* ] )?
220
        {
221
            $field_vis:vis $things:ident : [$EntryBuilder:ty] $(,)?
222
        }
223
        built: $Built:ty = $built:expr;
224
        default = $default:expr;
225
        $( item_build: $item_build:expr; )?
226
        $(#[ serde $serde_attrs:tt ] )+
227
    } => {
228
861298
        #[derive($crate::deps::educe::Educe, Clone, Debug)]
229
176
        #[derive($crate::deps::serde::Serialize, $crate::deps::serde::Deserialize)]
230
        #[educe(Default)]
231
        $(#[ serde $serde_attrs ])+
232
        $(#[ $docs_and_attrs ])*
233
        /// Wrapper struct to help derive_builder find the right types and methods
234
        ///
235
        /// This struct is not part of the configuration API.
236
        /// Refer to the containing structures for information on how to build the config.
237
        $vis struct $ListBuilder $( < $($generics)* > )?
238
        $( where $($where_clauses)* )?
239
        {
240
            /// The list, as overridden
241
            $field_vis $things: Option<Vec<$EntryBuilder>>,
242
        }
243

            
244
        impl $( < $($generics)* > )? $ListBuilder $( < $($generics)* > )?
245
        $( where $($where_clauses)* )?
246
        {
247
            /// Resolve this list to a list of built items.
248
            ///
249
            /// If the value is still the [`Default`],
250
            /// a built-in default list will be built and returned;
251
            /// otherwise each applicable item will be built,
252
            /// and the results collected into a single built list.
253
862403
            $vis fn build(&self) -> Result<$Built, $crate::ConfigBuildError> {
254
                let default_buffer;
255
862403
                let $things = match &self.$things {
256
550633
                    Some($things) => $things,
257
                    None => {
258
311770
                        default_buffer = Self::default_list();
259
311770
                        &default_buffer
260
                    }
261
                };
262

            
263
862402
                let $things = $things
264
36425
                    .iter()
265
36425
                    .map(
266
36425
                        $crate::deps::macro_first_nonempty!{
267
36425
                            [ $( $item_build )? ],
268
587289
                            [ |item| item.build() ],
269
36425
                        }
270
36425
                    )
271
36425
                    .collect::<Result<_, $crate::ConfigBuildError>>()?;
272
862399
                Ok($built)
273
556370
            }
274
316287

            
275
316287
            /// The default list
276
861717
            fn default_list() -> Vec<$EntryBuilder> {
277
861653
                 $default
278
861653
            }
279

            
280
            /// Resolve the list to the default if necessary and then return `&mut Vec`
281
793104
            $vis fn access(&mut self) -> &mut Vec<$EntryBuilder> {
282
793104
                self.$things.get_or_insert_with(Self::default_list)
283
793104
            }
284

            
285
            /// Resolve the list to the default if necessary and then return `&mut Vec`
286
2243
            $vis fn access_opt(&self) -> &Option<Vec<$EntryBuilder>> {
287
2243
                &self.$things
288
2243
            }
289

            
290
            /// Resolve the list to the default if necessary and then return `&mut Vec`
291
470
            $vis fn access_opt_mut(&mut self) -> &mut Option<Vec<$EntryBuilder>> {
292
470
                &mut self.$things
293
470
            }
294
        }
295
    };
296

            
297
    // Expand the version without `#[ serde $serde_attrs ]` into a call
298
    // which provides `#[serde(transparent)]`.
299
    //
300
    // We can't use `macro_first_nonempty!` because macro calls cannot be invoked
301
    // to generate attributes, only items, expressions, etc.
302
    {
303
        $(#[ $docs_and_attrs:meta ])*
304
        $vis:vis
305
        struct $ListBuilder:ident $( [ $($generics:tt)* ] )?
306
        $( where [ $($where_clauses:tt)* ] )?
307
        {
308
            $field_vis:vis $things:ident : [$EntryBuilder:ty] $(,)?
309
        }
310
        built: $Built:ty = $built:expr;
311
        default = $default:expr;
312
        $( item_build: $item_build:expr; )?
313
    } => {
314
        define_list_builder_helper! {
315
            $(#[ $docs_and_attrs ])*
316
            $vis
317
            struct $ListBuilder $( [ $($generics)* ] )?
318
            $( where [ $($where_clauses)* ] )?
319
            {
320
                $field_vis $things : [$EntryBuilder],
321
            }
322
            built: $Built = $built;
323
            default = $default;
324
            $( item_build: $item_build; )?
325
            #[serde(transparent)]
326
        }
327
    };
328
}
329

            
330
/// Define accessor methods for a configuration item which is a list
331
///
332
/// **See the [`list_builder` module documentation](crate::list_builder) for an overview.**
333
///
334
/// Generates the following methods for each specified field:
335
///
336
/// ```skip
337
/// impl $OuterBuilder {
338
///     pub fn $things(&mut self) -> &mut Vec<$EntryBuilder> { .. }
339
///     pub fn set_$things(&mut self, list: Vec<$EntryBuilder>) { .. }
340
///     pub fn opt_$things(&self) -> &Option<Vec<$EntryBuilder>> { .. }
341
///     pub fn opt_$things_mut>](&mut self) -> &mut Option<Vec<$EntryBuilder>> { .. }
342
/// }
343
/// ```
344
///
345
/// Each `$EntryBuilder` should have been defined by [`define_list_builder_helper`];
346
/// the method bodies from this macro rely on facilities which will beprovided by that macro.
347
///
348
/// You can call `define_list_builder_accessors` once for a particular `$OuterBuilder`,
349
/// with any number of fields with possibly different entry (`$EntryBuilder`) types.
350
#[macro_export]
351
macro_rules! define_list_builder_accessors {
352
    {
353
        struct $OuterBuilder:ty {
354
            $(
355
                $vis:vis $things:ident: [$EntryBuilder:ty],
356
            )*
357
        }
358
    } => {
359
        impl $OuterBuilder { $( $crate::deps::paste!{
360
            /// Access the being-built list (resolving default)
361
            ///
362
            /// If the field has not yet been set or accessed, the default list will be
363
            /// constructed and a mutable reference to the now-defaulted list of builders
364
            /// will be returned.
365
792896
            $vis fn $things(&mut self) -> &mut Vec<$EntryBuilder> {
366
                #[allow(unused_imports)]
367
                use $crate::list_builder::DirectDefaultEmptyListBuilderAccessors as _;
368
792896
                self.$things.access()
369
792896
            }
370

            
371
            /// Set the whole list (overriding the default)
372
192
            $vis fn [<set_ $things>](&mut self, list: Vec<$EntryBuilder>) {
373
                #[allow(unused_imports)]
374
                use $crate::list_builder::DirectDefaultEmptyListBuilderAccessors as _;
375
192
                *self.$things.access_opt_mut() = Some(list)
376
192
            }
377

            
378
            /// Inspect the being-built list (with default unresolved)
379
            ///
380
            /// If the list has not yet been set, or accessed, `&None` is returned.
381
1847
            $vis fn [<opt_ $things>](&self) -> &Option<Vec<$EntryBuilder>> {
382
                #[allow(unused_imports)]
383
                use $crate::list_builder::DirectDefaultEmptyListBuilderAccessors as _;
384
1847
                self.$things.access_opt()
385
1847
            }
386

            
387
            /// Mutably access the being-built list (with default unresolved)
388
            ///
389
            /// If the list has not yet been set, or accessed, `&mut None` is returned.
390
2
            $vis fn [<opt_ $things _mut>](&mut self) -> &mut Option<Vec<$EntryBuilder>> {
391
                #[allow(unused_imports)]
392
                use $crate::list_builder::DirectDefaultEmptyListBuilderAccessors as _;
393
2
                self.$things.access_opt_mut()
394
2
            }
395
        } )* }
396
    }
397
}
398

            
399
/// Extension trait, an alternative to `define_list_builder_helper`
400
///
401
/// Useful for a handwritten `Builder` which wants to contain a list,
402
/// which is an `Option<Vec<ItemBuilder>>`.
403
///
404
/// # Example
405
///
406
/// ```
407
/// use tor_config::define_list_builder_accessors;
408
///
409
/// #[derive(Default)]
410
/// struct WombatBuilder {
411
///     leg_lengths: Option<Vec<u32>>,
412
/// }
413
///
414
/// define_list_builder_accessors! {
415
///     struct WombatBuilder {
416
///         leg_lengths: [u32],
417
///     }
418
/// }
419
///
420
/// let mut wb = WombatBuilder::default();
421
/// wb.leg_lengths().push(42);
422
///
423
/// assert_eq!(wb.leg_lengths, Some(vec![42]));
424
/// ```
425
///
426
/// It is not necessary to `use` this trait anywhere in your code;
427
/// the macro `define_list_builder_accessors` arranges to have it in scope where it needs it.
428
pub trait DirectDefaultEmptyListBuilderAccessors {
429
    /// Entry type
430
    type T;
431
    /// Get access to the `Vec`, defaulting it
432
    fn access(&mut self) -> &mut Vec<Self::T>;
433
    /// Get access to the `Option<Vec>`
434
    fn access_opt(&self) -> &Option<Vec<Self::T>>;
435
    /// Get mutable access to the `Option<Vec>`
436
    fn access_opt_mut(&mut self) -> &mut Option<Vec<Self::T>>;
437
}
438
impl<T> DirectDefaultEmptyListBuilderAccessors for Option<Vec<T>> {
439
    type T = T;
440
30
    fn access(&mut self) -> &mut Vec<T> {
441
30
        self.get_or_insert_with(Vec::new)
442
30
    }
443
    fn access_opt(&self) -> &Option<Vec<T>> {
444
        self
445
    }
446
    fn access_opt_mut(&mut self) -> &mut Option<Vec<T>> {
447
        self
448
    }
449
}
450

            
451
define_list_builder_helper! {
452
    /// List of `T`, a straightforward type, being built as part of the configuration
453
    ///
454
    /// The default is the empty list.
455
    ///
456
    /// ### Example
457
    ///
458
    /// ```
459
    /// use derive_builder::Builder;
460
    /// use serde::{Deserialize, Serialize};
461
    /// use tor_config::ConfigBuildError;
462
    /// use tor_config::{define_list_builder_accessors, list_builder::VecBuilder};
463
    /// use std::net::SocketAddr;
464
    ///
465
    /// #[derive(Debug, Clone, Builder)]
466
    /// #[builder(build_fn(error = "ConfigBuildError"))]
467
    /// #[builder(derive(Debug, Serialize, Deserialize))]
468
    /// pub struct FallbackDir {
469
    ///     #[builder(sub_builder(fn_name = "build"), setter(custom))]
470
    ///     orports: Vec<SocketAddr>,
471
    /// }
472
    ///
473
    /// define_list_builder_accessors! {
474
    ///     struct FallbackDirBuilder {
475
    ///         pub orports: [SocketAddr],
476
    ///     }
477
    /// }
478
    ///
479
    /// let mut bld = FallbackDirBuilder::default();
480
    /// bld.orports().push("[2001:db8:0::42]:12".parse().unwrap());
481
    /// assert_eq!( bld.build().unwrap().orports[0].to_string(),
482
    ///             "[2001:db8::42]:12" );
483
    /// ```
484
    pub struct VecBuilder[T] where [T: Clone] {
485
        values: [T],
486
    }
487
    built: Vec<T> = values;
488
    default = vec![];
489
789113
    item_build: |item| Ok(item.clone());
490
}
491

            
492
/// Configuration item specifiable as a list, or a single multi-line string
493
///
494
/// If a list is supplied, they are deserialized as builders.
495
/// If a single string is supplied, it is split into lines, and `#`-comments
496
/// and blank lines and whitespace are stripped, and then each line is parsed
497
/// as a builder.
498
/// (Eventually, the builders will be built.)
499
///
500
/// For use with `sub_builder` and [`define_list_builder_helper`],
501
/// with `#[serde(try_from)]` and `#[serde(into)]`.
502
///
503
/// # Example
504
///
505
/// ```
506
/// use derive_builder::Builder;
507
/// use serde::{Deserialize, Serialize};
508
/// use tor_config::{ConfigBuildError, MultilineListBuilder};
509
/// use tor_config::convert_helper_via_multi_line_list_builder;
510
/// use tor_config::{define_list_builder_accessors, define_list_builder_helper};
511
/// use tor_config::impl_standard_builder;
512
///
513
/// # fn generate_random<T: Default>() -> T { Default::default() }
514
///
515
/// #[derive(Debug, Clone, Builder, Eq, PartialEq)]
516
/// #[builder(build_fn(error = "ConfigBuildError"))]
517
/// #[builder(derive(Debug, Serialize, Deserialize))]
518
/// #[non_exhaustive]
519
/// pub struct LotteryConfig {
520
///     /// What numbers should win the lottery?  Setting this is lottery fraud.
521
///     #[builder(sub_builder, setter(custom))]
522
///     #[builder_field_attr(serde(default))]
523
///     winners: LotteryNumberList,
524
/// }
525
/// impl_standard_builder! { LotteryConfig }
526
///
527
/// /// List of lottery winners
528
/// //
529
/// // This type alias arranges that we can put `LotteryNumberList` in `LotteryConfig`
530
/// // and have derive_builder put a `LotteryNumberListBuilder` in `LotteryConfigBuilder`.
531
/// pub type LotteryNumberList = Vec<u16>;
532
///
533
/// define_list_builder_helper! {
534
///     struct LotteryNumberListBuilder {
535
///         numbers: [u16],
536
///     }
537
///     built: LotteryNumberList = numbers;
538
///     default = generate_random();
539
///     item_build: |number| Ok(*number);
540
///     #[serde(try_from="MultilineListBuilder<u16>")]
541
///     #[serde(into="MultilineListBuilder<u16>")]
542
/// }
543
///
544
/// convert_helper_via_multi_line_list_builder! {
545
///     struct LotteryNumberListBuilder {
546
///         numbers: [u16],
547
///     }
548
/// }
549
///
550
/// define_list_builder_accessors! {
551
///     struct LotteryConfigBuilder {
552
///         pub winners: [u16],
553
///     }
554
/// }
555
///
556
/// let lc: LotteryConfigBuilder = toml::from_str(r#"winners = [1,2,3]"#).unwrap();
557
/// let lc = lc.build().unwrap();
558
/// assert_eq!{ lc.winners, [1,2,3] }
559
///
560
/// let lc = r#"
561
/// winners = '''
562
///   ## Enny tells us this is the ticket they bought:
563
///
564
///   4
565
///   5
566
///   6
567
/// '''
568
/// "#;
569
/// let lc: LotteryConfigBuilder = toml::from_str(lc).unwrap();
570
/// let lc = lc.build().unwrap();
571
/// assert_eq!{ lc.winners, [4,5,6] }
572
/// ```
573
#[derive(Clone, Debug, Default, Serialize)]
574
#[serde(untagged)]
575
#[non_exhaustive]
576
pub enum MultilineListBuilder<EB> {
577
    /// Config key not present
578
    #[default]
579
    Unspecified,
580

            
581
    /// Config key was a string which is to be parsed line-by-line
582
    String(String),
583

            
584
    /// Config key was a list of the individual entry builders
585
    List(Vec<EB>),
586
}
587

            
588
/// Error from trying to parse a MultilineListBuilder as a list of particular items
589
///
590
/// Usually, this error is generated during deserialization.
591
#[derive(Error, Debug, Clone)]
592
#[error("multi-line string, line/item {item_number}: could not parse {line:?}: {error}")]
593
#[non_exhaustive]
594
pub struct MultilineListBuilderError<E: std::error::Error + Clone + Send + Sync> {
595
    /// The line number (in the multi-line text string) that could not be parsed
596
    ///
597
    /// Starting at 1.
598
    item_number: usize,
599

            
600
    /// The line that could not be parsed
601
    line: String,
602

            
603
    /// The parse error from `FromStr`
604
    ///
605
    /// This is not a `source` because we want to include it in the `Display`
606
    /// implementation so that serde errors are useful.
607
    error: E,
608
}
609

            
610
// We could derive this with `#[serde(untagged)]` but that produces quite terrible error
611
// messages, which do not reproduce the error messages from any of the variants.
612
//
613
// Instead, have a manual implementation, which can see whether the input is a list or a string.
614
impl<'de, EB: Deserialize<'de>> Deserialize<'de> for MultilineListBuilder<EB> {
615
48
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
616
48
    where
617
48
        D: Deserializer<'de>,
618
48
    {
619
48
        deserializer.deserialize_any(MllbVisitor::default())
620
48
    }
621
}
622

            
623
/// Visitor for deserialize_any for [`MultilineListBuilder`]
624
48
#[derive(Educe)]
625
#[educe(Default)]
626
struct MllbVisitor<EB> {
627
    /// Variance: this visitor constructs `EB`s
628
    ret: PhantomData<fn() -> EB>,
629
}
630

            
631
impl<'de, EB: Deserialize<'de>> serde::de::Visitor<'de> for MllbVisitor<EB> {
632
    type Value = MultilineListBuilder<EB>;
633

            
634
    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
635
        write!(f, "list of items, or multi-line string")
636
    }
637

            
638
40
    fn visit_seq<A: serde::de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
639
40
        let mut v = vec![];
640
82
        while let Some(e) = seq.next_element()? {
641
42
            v.push(e);
642
42
        }
643
38
        Ok(MultilineListBuilder::List(v))
644
40
    }
645

            
646
2
    fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
647
2
        self.visit_string(v.to_owned())
648
2
    }
649
8
    fn visit_string<E: serde::de::Error>(self, v: String) -> Result<Self::Value, E> {
650
8
        Ok(MultilineListBuilder::String(v))
651
8
    }
652

            
653
    fn visit_none<E: serde::de::Error>(self) -> Result<Self::Value, E> {
654
        Ok(MultilineListBuilder::Unspecified)
655
    }
656
}
657

            
658
impl<EB> From<Option<Vec<EB>>> for MultilineListBuilder<EB> {
659
56
    fn from(list: Option<Vec<EB>>) -> Self {
660
        use MultilineListBuilder as MlLB;
661
56
        match list {
662
56
            None => MlLB::Unspecified,
663
            Some(list) => MlLB::List(list),
664
        }
665
56
    }
666
}
667

            
668
impl<EB> TryInto<Option<Vec<EB>>> for MultilineListBuilder<EB>
669
where
670
    EB: FromStr,
671
    EB::Err: std::error::Error + Clone + Send + Sync,
672
{
673
    type Error = MultilineListBuilderError<EB::Err>;
674
126
    fn try_into(self) -> Result<Option<Vec<EB>>, Self::Error> {
675
        use MultilineListBuilder as MlLB;
676

            
677
        /// Helper for parsing each line of `iter` and collecting the results
678
34
        fn parse_collect<'s, I>(
679
34
            iter: impl Iterator<Item = (usize, &'s str)>,
680
34
        ) -> Result<Option<Vec<I>>, MultilineListBuilderError<I::Err>>
681
34
        where
682
34
            I: FromStr,
683
34
            I::Err: std::error::Error + Clone + Send + Sync,
684
34
        {
685
34
            Ok(Some(
686
90
                iter.map(|(i, l)| {
687
90
                    l.parse().map_err(|error| MultilineListBuilderError {
688
2
                        item_number: i + 1,
689
2
                        line: l.to_owned(),
690
2
                        error,
691
90
                    })
692
90
                })
693
34
                .try_collect()?,
694
            ))
695
34
        }
696

            
697
126
        Ok(match self {
698
2
            MlLB::Unspecified => None,
699
90
            MlLB::List(list) => Some(list),
700
34
            MlLB::String(s) => parse_collect(
701
34
                s.lines()
702
34
                    .enumerate()
703
118
                    .map(|(i, l)| (i, l.trim()))
704
118
                    .filter(|(_, l)| !(l.starts_with('#') || l.is_empty())),
705
34
            )?,
706
        })
707
126
    }
708
}
709

            
710
/// Implement `TryFrom<MultilineListBuilder>` and `Into<MultilineListBuilder>` for $Builder.
711
///
712
/// The input syntax is the `struct` part of that for `define_list_builder_helper`.
713
/// `$EntryBuilder` must implement `FromStr`.
714
//
715
// This is a macro because a helper trait to enable blanket impl would have to provide
716
// access to `$things`, defeating much of the point.
717
#[macro_export]
718
macro_rules! convert_helper_via_multi_line_list_builder { {
719
    struct $ListBuilder:ident { $things:ident: [$EntryBuilder:ty] $(,)? }
720
} => {
721
    impl std::convert::TryFrom<$crate::MultilineListBuilder<$EntryBuilder>> for $ListBuilder {
722
        type Error = $crate::MultilineListBuilderError<<$EntryBuilder as std::str::FromStr>::Err>;
723

            
724
114
        fn try_from(mllb: $crate::MultilineListBuilder<$EntryBuilder>)
725
114
                    -> std::result::Result<$ListBuilder, Self::Error> {
726
114
            Ok($ListBuilder { $things: mllb.try_into()? })
727
96
        }
728
96
    }
729
96

            
730
96
    impl From<$ListBuilder> for MultilineListBuilder<$EntryBuilder> {
731
98
        fn from(lb: $ListBuilder) -> MultilineListBuilder<$EntryBuilder> {
732
56
            lb.$things.into()
733
56
        }
734
    }
735
} }
736

            
737
#[cfg(test)]
738
mod test {
739
    // @@ begin test lint list maintained by maint/add_warning @@
740
    #![allow(clippy::bool_assert_comparison)]
741
    #![allow(clippy::clone_on_copy)]
742
    #![allow(clippy::dbg_macro)]
743
    #![allow(clippy::mixed_attributes_style)]
744
    #![allow(clippy::print_stderr)]
745
    #![allow(clippy::print_stdout)]
746
    #![allow(clippy::single_char_pattern)]
747
    #![allow(clippy::unwrap_used)]
748
    #![allow(clippy::unchecked_duration_subtraction)]
749
    #![allow(clippy::useless_vec)]
750
    #![allow(clippy::needless_pass_by_value)]
751
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
752
    use super::*;
753
    use derive_builder::Builder;
754

            
755
    #[derive(Eq, PartialEq, Builder)]
756
    #[builder(derive(Deserialize))]
757
    struct Outer {
758
        #[builder(sub_builder, setter(custom))]
759
        list: List,
760
    }
761

            
762
    define_list_builder_accessors! {
763
        struct OuterBuilder {
764
            list: [char],
765
        }
766
    }
767

            
768
    type List = Vec<char>;
769

            
770
    define_list_builder_helper! {
771
        struct ListBuilder {
772
            list: [char],
773
        }
774
        built: List = list;
775
        default = vec!['a'];
776
        item_build: |&c| Ok(c);
777
    }
778

            
779
    #[test]
780
    fn nonempty_default() {
781
        let mut b = OuterBuilder::default();
782
        assert!(b.opt_list().is_none());
783
        assert_eq! { b.build().expect("build failed").list, ['a'] };
784

            
785
        b.list().push('b');
786
        assert!(b.opt_list().is_some());
787
        assert_eq! { b.build().expect("build failed").list, ['a', 'b'] };
788

            
789
        for mut b in [b.clone(), OuterBuilder::default()] {
790
            b.set_list(vec!['x', 'y']);
791
            assert!(b.opt_list().is_some());
792
            assert_eq! { b.build().expect("build failed").list, ['x', 'y'] };
793
        }
794

            
795
        *b.opt_list_mut() = None;
796
        assert_eq! { b.build().expect("build failed").list, ['a'] };
797
    }
798

            
799
    #[test]
800
    fn vecbuilder() {
801
        // Minimal test, since rustdoc tests seem not to be finding the documentation inside
802
        // the declaration of VecBuilder.  (Or at least that's what the coverage says.)
803
        let mut b = VecBuilder::<u32>::default();
804
        b.access().push(1);
805
        b.access().push(2);
806
        b.access().push(3);
807
        assert_eq!(b.build().unwrap(), vec![1, 2, 3]);
808
    }
809

            
810
    #[test]
811
    fn deser() {
812
        let o: OuterBuilder = toml::from_str("list = ['x','y']").unwrap();
813
        let o = o.build().unwrap();
814
        assert_eq!(o.list, ['x', 'y']);
815

            
816
        #[derive(Deserialize, Debug)]
817
        struct OuterWithMllb {
818
            #[serde(default)]
819
            list: MultilineListBuilder<u32>,
820
        }
821

            
822
        let parse_ok = |s: &str| {
823
            let o: OuterWithMllb = toml::from_str(s).unwrap();
824
            let l: Option<Vec<_>> = o.list.try_into().unwrap();
825
            l
826
        };
827

            
828
        let l = parse_ok("");
829
        assert!(l.is_none());
830

            
831
        let l = parse_ok("list = []");
832
        assert!(l.unwrap().is_empty());
833

            
834
        let l = parse_ok("list = [12,42]");
835
        assert_eq!(l.unwrap(), [12, 42]);
836

            
837
        let l = parse_ok(r#"list = """#);
838
        assert!(l.unwrap().is_empty());
839

            
840
        let l = parse_ok("list = \"\"\"\n12\n42\n\"\"\"\n");
841
        assert_eq!(l.unwrap(), [12, 42]);
842

            
843
        let e = toml::from_str::<OuterWithMllb>("list = [\"fail\"]")
844
            .unwrap_err()
845
            .to_string();
846
        assert!(dbg!(e).contains(r#"invalid type: string "fail", expected u32"#));
847

            
848
        let o = toml::from_str::<OuterWithMllb>("list = \"\"\"\nfail\n\"\"\"").unwrap();
849
        let l: Result<Option<Vec<_>>, _> = o.list.try_into();
850
        let e = l.unwrap_err().to_string();
851
        assert_eq!(e, "multi-line string, line/item 1: could not parse \"fail\": invalid digit found in string");
852
    }
853
}