Skip to main content

tor_config/
list_builder.rs

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
136use std::fmt;
137use std::marker::PhantomData;
138use std::str::FromStr;
139
140use educe::Educe;
141use itertools::Itertools;
142use serde::{Deserialize, Deserializer, Serialize};
143use thiserror::Error;
144
145pub use crate::define_list_builder_accessors;
146pub 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]
214macro_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        #[derive(Clone, Debug)]
229        #[derive($crate::deps::serde::Serialize, $crate::deps::serde::Deserialize)]
230        $(#[ serde $serde_attrs ])+
231        $(#[ $docs_and_attrs ])*
232        /// Wrapper struct to help derive_builder find the right types and methods
233        ///
234        /// This struct is not part of the configuration API.
235        /// Refer to the containing structures for information on how to build the config.
236        $vis struct $ListBuilder $( < $($generics)* > )?
237        $( where $($where_clauses)* )?
238        {
239            /// The list, as overridden
240            $field_vis $things: Option<Vec<$EntryBuilder>>,
241        }
242
243        impl $( <$($generics)*> )? Default for $ListBuilder$( < $($generics)* > )?
244        $( where $($where_clauses)* )?
245        {
246            fn default() -> Self {
247                Self {
248                    $things: None
249                }
250            }
251        }
252
253        impl $( < $($generics)* > )? $ListBuilder $( < $($generics)* > )?
254        $( where $($where_clauses)* )?
255        {
256            /// Resolve this list to a list of built items.
257            ///
258            /// If the value is still the [`Default`],
259            /// a built-in default list will be built and returned;
260            /// otherwise each applicable item will be built,
261            /// and the results collected into a single built list.
262            $vis fn build(&self) -> Result<$Built, $crate::ConfigBuildError> {
263                let default_buffer;
264                let $things = match &self.$things {
265                    Some($things) => $things,
266                    None => {
267                        default_buffer = Self::default_list();
268                        &default_buffer
269                    }
270                };
271
272                let $things = $things
273                    .iter()
274                    .map(
275                        $crate::deps::macro_first_nonempty!{
276                            [ $( $item_build )? ],
277                            [ |item| item.build() ],
278                        }
279                    )
280                    .collect::<Result<_, $crate::ConfigBuildError>>()?;
281                Ok($built)
282            }
283
284            /// The default list
285            fn default_list() -> Vec<$EntryBuilder> {
286                 $default
287            }
288
289            /// Resolve the list to the default if necessary and then return `&mut Vec`
290            $vis fn access(&mut self) -> &mut Vec<$EntryBuilder> {
291                self.$things.get_or_insert_with(Self::default_list)
292            }
293
294            /// Resolve the list to the default if necessary and then return `&mut Vec`
295            $vis fn access_opt(&self) -> &Option<Vec<$EntryBuilder>> {
296                &self.$things
297            }
298
299            /// Resolve the list to the default if necessary and then return `&mut Vec`
300            $vis fn access_opt_mut(&mut self) -> &mut Option<Vec<$EntryBuilder>> {
301                &mut self.$things
302            }
303        }
304
305        impl $( < $($generics)* > )? $crate::extend_builder::ExtendBuilder
306        for $ListBuilder $( < $($generics)* > )?
307        $( where $($where_clauses)* )? {
308            fn extend_from(&mut self, other: Self, strategy: $crate::extend_builder::ExtendStrategy) {
309                match strategy {
310                    $crate::extend_builder::ExtendStrategy::ReplaceLists =>
311                        *self = other,
312                }
313            }
314        }
315    };
316
317    // Expand the version without `#[ serde $serde_attrs ]` into a call
318    // which provides `#[serde(transparent)]`.
319    //
320    // We can't use `macro_first_nonempty!` because macro calls cannot be invoked
321    // to generate attributes, only items, expressions, etc.
322    {
323        $(#[ $docs_and_attrs:meta ])*
324        $vis:vis
325        struct $ListBuilder:ident $( [ $($generics:tt)* ] )?
326        $( where [ $($where_clauses:tt)* ] )?
327        {
328            $field_vis:vis $things:ident : [$EntryBuilder:ty] $(,)?
329        }
330        built: $Built:ty = $built:expr;
331        default = $default:expr;
332        $( item_build: $item_build:expr; )?
333    } => {
334        $crate::define_list_builder_helper! {
335            $(#[ $docs_and_attrs ])*
336            $vis
337            struct $ListBuilder $( [ $($generics)* ] )?
338            $( where [ $($where_clauses)* ] )?
339            {
340                $field_vis $things : [$EntryBuilder],
341            }
342            built: $Built = $built;
343            default = $default;
344            $( item_build: $item_build; )?
345            #[serde(transparent)]
346        }
347    };
348}
349
350/// Define accessor methods for a configuration item which is a list
351///
352/// **See the [`list_builder` module documentation](crate::list_builder) for an overview.**
353///
354/// Generates the following methods for each specified field:
355///
356/// ```skip
357/// impl $OuterBuilder {
358///     pub fn $things(&mut self) -> &mut Vec<$EntryBuilder> { .. }
359///     pub fn set_$things(&mut self, list: Vec<$EntryBuilder>) { .. }
360///     pub fn opt_$things(&self) -> &Option<Vec<$EntryBuilder>> { .. }
361///     pub fn opt_$things_mut>](&mut self) -> &mut Option<Vec<$EntryBuilder>> { .. }
362/// }
363/// ```
364///
365/// Each `$EntryBuilder` should have been defined by [`define_list_builder_helper`];
366/// the method bodies from this macro rely on facilities which will beprovided by that macro.
367///
368/// You can call `define_list_builder_accessors` once for a particular `$OuterBuilder`,
369/// with any number of fields with possibly different entry (`$EntryBuilder`) types.
370#[macro_export]
371macro_rules! define_list_builder_accessors {
372    {
373        struct $OuterBuilder:ty {
374            $(
375                $vis:vis $things:ident: [$EntryBuilder:ty],
376            )*
377        }
378    } => {
379        #[allow(dead_code)]
380        impl $OuterBuilder { $( $crate::deps::paste!{
381            /// Access the being-built list (resolving default)
382            ///
383            /// If the field has not yet been set or accessed, the default list will be
384            /// constructed and a mutable reference to the now-defaulted list of builders
385            /// will be returned.
386            $vis fn $things(&mut self) -> &mut Vec<$EntryBuilder> {
387                #[allow(unused_imports)]
388                use $crate::list_builder::DirectDefaultEmptyListBuilderAccessors as _;
389                self.$things.access()
390            }
391
392            /// Set the whole list (overriding the default)
393            $vis fn [<set_ $things>](&mut self, list: Vec<$EntryBuilder>) {
394                #[allow(unused_imports)]
395                use $crate::list_builder::DirectDefaultEmptyListBuilderAccessors as _;
396                *self.$things.access_opt_mut() = Some(list)
397            }
398
399            /// Inspect the being-built list (with default unresolved)
400            ///
401            /// If the list has not yet been set, or accessed, `&None` is returned.
402            $vis fn [<opt_ $things>](&self) -> &Option<Vec<$EntryBuilder>> {
403                #[allow(unused_imports)]
404                use $crate::list_builder::DirectDefaultEmptyListBuilderAccessors as _;
405                self.$things.access_opt()
406            }
407
408            /// Mutably access the being-built list (with default unresolved)
409            ///
410            /// If the list has not yet been set, or accessed, `&mut None` is returned.
411            $vis fn [<opt_ $things _mut>](&mut self) -> &mut Option<Vec<$EntryBuilder>> {
412                #[allow(unused_imports)]
413                use $crate::list_builder::DirectDefaultEmptyListBuilderAccessors as _;
414                self.$things.access_opt_mut()
415            }
416        } )* }
417    }
418}
419
420/// Extension trait, an alternative to `define_list_builder_helper`
421///
422/// Useful for a handwritten `Builder` which wants to contain a list,
423/// which is an `Option<Vec<ItemBuilder>>`.
424///
425/// # Example
426///
427/// ```
428/// use tor_config::define_list_builder_accessors;
429///
430/// #[derive(Default)]
431/// struct WombatBuilder {
432///     leg_lengths: Option<Vec<u32>>,
433/// }
434///
435/// define_list_builder_accessors! {
436///     struct WombatBuilder {
437///         leg_lengths: [u32],
438///     }
439/// }
440///
441/// let mut wb = WombatBuilder::default();
442/// wb.leg_lengths().push(42);
443///
444/// assert_eq!(wb.leg_lengths, Some(vec![42]));
445/// ```
446///
447/// It is not necessary to `use` this trait anywhere in your code;
448/// the macro `define_list_builder_accessors` arranges to have it in scope where it needs it.
449pub trait DirectDefaultEmptyListBuilderAccessors {
450    /// Entry type
451    type T;
452    /// Get access to the `Vec`, defaulting it
453    fn access(&mut self) -> &mut Vec<Self::T>;
454    /// Get access to the `Option<Vec>`
455    fn access_opt(&self) -> &Option<Vec<Self::T>>;
456    /// Get mutable access to the `Option<Vec>`
457    fn access_opt_mut(&mut self) -> &mut Option<Vec<Self::T>>;
458}
459impl<T> DirectDefaultEmptyListBuilderAccessors for Option<Vec<T>> {
460    type T = T;
461    fn access(&mut self) -> &mut Vec<T> {
462        self.get_or_insert_with(Vec::new)
463    }
464    fn access_opt(&self) -> &Option<Vec<T>> {
465        self
466    }
467    fn access_opt_mut(&mut self) -> &mut Option<Vec<T>> {
468        self
469    }
470}
471
472define_list_builder_helper! {
473    /// List of `T`, a straightforward type, being built as part of the configuration
474    ///
475    /// The default is the empty list.
476    ///
477    /// ### Example
478    ///
479    /// ```
480    /// use derive_builder::Builder;
481    /// use serde::{Deserialize, Serialize};
482    /// use tor_config::ConfigBuildError;
483    /// use tor_config::{define_list_builder_accessors, list_builder::VecBuilder};
484    /// use std::net::SocketAddr;
485    ///
486    /// #[derive(Debug, Clone, Builder)]
487    /// #[builder(build_fn(error = "ConfigBuildError"))]
488    /// #[builder(derive(Debug, Serialize, Deserialize))]
489    /// pub struct FallbackDir {
490    ///     #[builder(sub_builder(fn_name = "build"), setter(custom))]
491    ///     orports: Vec<SocketAddr>,
492    /// }
493    ///
494    /// define_list_builder_accessors! {
495    ///     struct FallbackDirBuilder {
496    ///         pub orports: [SocketAddr],
497    ///     }
498    /// }
499    ///
500    /// let mut bld = FallbackDirBuilder::default();
501    /// bld.orports().push("[2001:db8:0::42]:12".parse().unwrap());
502    /// assert_eq!( bld.build().unwrap().orports[0].to_string(),
503    ///             "[2001:db8::42]:12" );
504    /// ```
505    pub struct VecBuilder[T] where [T: Clone] {
506        values: [T],
507    }
508    built: Vec<T> = values;
509    default = vec![];
510    item_build: |item| Ok(item.clone());
511}
512
513/// Configuration item specifiable as a list, or a single multi-line string
514///
515/// If a list is supplied, they are deserialized as builders.
516/// If a single string is supplied, it is split into lines, and `#`-comments
517/// and blank lines and whitespace are stripped, and then each line is parsed
518/// as a builder.
519/// (Eventually, the builders will be built.)
520///
521/// For use with `sub_builder` and [`define_list_builder_helper`],
522/// with `#[serde(try_from)]` and `#[serde(into)]`.
523///
524/// # Example
525///
526/// ```
527/// use derive_builder::Builder;
528/// use serde::{Deserialize, Serialize};
529/// use tor_config::{ConfigBuildError, MultilineListBuilder};
530/// use tor_config::convert_helper_via_multi_line_list_builder;
531/// use tor_config::{define_list_builder_accessors, define_list_builder_helper};
532/// use tor_config::impl_standard_builder;
533///
534/// # fn generate_random<T: Default>() -> T { Default::default() }
535///
536/// #[derive(Debug, Clone, Builder, Eq, PartialEq)]
537/// #[builder(build_fn(error = "ConfigBuildError"))]
538/// #[builder(derive(Debug, Serialize, Deserialize))]
539/// #[non_exhaustive]
540/// pub struct LotteryConfig {
541///     /// What numbers should win the lottery?  Setting this is lottery fraud.
542///     #[builder(sub_builder, setter(custom))]
543///     #[builder_field_attr(serde(default))]
544///     winners: LotteryNumberList,
545/// }
546/// impl_standard_builder! { LotteryConfig }
547///
548/// /// List of lottery winners
549/// //
550/// // This type alias arranges that we can put `LotteryNumberList` in `LotteryConfig`
551/// // and have derive_builder put a `LotteryNumberListBuilder` in `LotteryConfigBuilder`.
552/// pub type LotteryNumberList = Vec<u16>;
553///
554/// define_list_builder_helper! {
555///     struct LotteryNumberListBuilder {
556///         numbers: [u16],
557///     }
558///     built: LotteryNumberList = numbers;
559///     default = generate_random();
560///     item_build: |number| Ok(*number);
561///     #[serde(try_from="MultilineListBuilder<u16>")]
562///     #[serde(into="MultilineListBuilder<u16>")]
563/// }
564///
565/// convert_helper_via_multi_line_list_builder! {
566///     struct LotteryNumberListBuilder {
567///         numbers: [u16],
568///     }
569/// }
570///
571/// define_list_builder_accessors! {
572///     struct LotteryConfigBuilder {
573///         pub winners: [u16],
574///     }
575/// }
576///
577/// let lc: LotteryConfigBuilder = toml::from_str(r#"winners = [1,2,3]"#).unwrap();
578/// let lc = lc.build().unwrap();
579/// assert_eq!{ lc.winners, [1,2,3] }
580///
581/// let lc = r#"
582/// winners = '''
583///   ## Enny tells us this is the ticket they bought:
584///
585///   4
586///   5
587///   6
588/// '''
589/// "#;
590/// let lc: LotteryConfigBuilder = toml::from_str(lc).unwrap();
591/// let lc = lc.build().unwrap();
592/// assert_eq!{ lc.winners, [4,5,6] }
593/// ```
594#[derive(Clone, Debug, Default, Serialize)]
595#[serde(untagged)]
596#[non_exhaustive]
597pub enum MultilineListBuilder<EB> {
598    /// Config key not present
599    #[default]
600    Unspecified,
601
602    /// Config key was a string which is to be parsed line-by-line
603    String(String),
604
605    /// Config key was a list of the individual entry builders
606    List(Vec<EB>),
607}
608
609/// Error from trying to parse a MultilineListBuilder as a list of particular items
610///
611/// Usually, this error is generated during deserialization.
612#[derive(Error, Debug, Clone)]
613#[error("multi-line string, line/item {item_number}: could not parse {line:?}: {error}")]
614#[non_exhaustive]
615pub struct MultilineListBuilderError<E: std::error::Error + Clone + Send + Sync> {
616    /// The line number (in the multi-line text string) that could not be parsed
617    ///
618    /// Starting at 1.
619    item_number: usize,
620
621    /// The line that could not be parsed
622    line: String,
623
624    /// The parse error from `FromStr`
625    ///
626    /// This is not a `source` because we want to include it in the `Display`
627    /// implementation so that serde errors are useful.
628    error: E,
629}
630
631// We could derive this with `#[serde(untagged)]` but that produces quite terrible error
632// messages, which do not reproduce the error messages from any of the variants.
633//
634// Instead, have a manual implementation, which can see whether the input is a list or a string.
635impl<'de, EB: Deserialize<'de>> Deserialize<'de> for MultilineListBuilder<EB> {
636    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
637    where
638        D: Deserializer<'de>,
639    {
640        deserializer.deserialize_any(MllbVisitor::default())
641    }
642}
643
644/// Visitor for deserialize_any for [`MultilineListBuilder`]
645#[derive(Educe)]
646#[educe(Default)]
647struct MllbVisitor<EB> {
648    /// Variance: this visitor constructs `EB`s
649    ret: PhantomData<fn() -> EB>,
650}
651
652impl<'de, EB: Deserialize<'de>> serde::de::Visitor<'de> for MllbVisitor<EB> {
653    type Value = MultilineListBuilder<EB>;
654
655    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
656        write!(f, "list of items, or multi-line string")
657    }
658
659    fn visit_seq<A: serde::de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
660        let mut v = vec![];
661        while let Some(e) = seq.next_element()? {
662            v.push(e);
663        }
664        Ok(MultilineListBuilder::List(v))
665    }
666
667    fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
668        self.visit_string(v.to_owned())
669    }
670    fn visit_string<E: serde::de::Error>(self, v: String) -> Result<Self::Value, E> {
671        Ok(MultilineListBuilder::String(v))
672    }
673
674    fn visit_none<E: serde::de::Error>(self) -> Result<Self::Value, E> {
675        Ok(MultilineListBuilder::Unspecified)
676    }
677}
678
679impl<EB> From<Option<Vec<EB>>> for MultilineListBuilder<EB> {
680    fn from(list: Option<Vec<EB>>) -> Self {
681        use MultilineListBuilder as MlLB;
682        match list {
683            None => MlLB::Unspecified,
684            Some(list) => MlLB::List(list),
685        }
686    }
687}
688
689impl<EB> TryInto<Option<Vec<EB>>> for MultilineListBuilder<EB>
690where
691    EB: FromStr,
692    EB::Err: std::error::Error + Clone + Send + Sync,
693{
694    type Error = MultilineListBuilderError<EB::Err>;
695    fn try_into(self) -> Result<Option<Vec<EB>>, Self::Error> {
696        use MultilineListBuilder as MlLB;
697
698        /// Helper for parsing each line of `iter` and collecting the results
699        fn parse_collect<'s, I>(
700            iter: impl Iterator<Item = (usize, &'s str)>,
701        ) -> Result<Option<Vec<I>>, MultilineListBuilderError<I::Err>>
702        where
703            I: FromStr,
704            I::Err: std::error::Error + Clone + Send + Sync,
705        {
706            Ok(Some(
707                iter.map(|(i, l)| {
708                    l.parse().map_err(|error| MultilineListBuilderError {
709                        item_number: i + 1,
710                        line: l.to_owned(),
711                        error,
712                    })
713                })
714                .try_collect()?,
715            ))
716        }
717
718        Ok(match self {
719            MlLB::Unspecified => None,
720            MlLB::List(list) => Some(list),
721            MlLB::String(s) => parse_collect(
722                s.lines()
723                    .enumerate()
724                    .map(|(i, l)| (i, l.trim()))
725                    .filter(|(_, l)| !(l.starts_with('#') || l.is_empty())),
726            )?,
727        })
728    }
729}
730
731/// Implement `TryFrom<MultilineListBuilder>` and `Into<MultilineListBuilder>` for $Builder.
732///
733/// The input syntax is the `struct` part of that for `define_list_builder_helper`.
734/// `$EntryBuilder` must implement `FromStr`.
735//
736// This is a macro because a helper trait to enable blanket impl would have to provide
737// access to `$things`, defeating much of the point.
738#[macro_export]
739macro_rules! convert_helper_via_multi_line_list_builder { {
740    struct $ListBuilder:ident { $things:ident: [$EntryBuilder:ty] $(,)? }
741} => {
742    impl std::convert::TryFrom<$crate::MultilineListBuilder<$EntryBuilder>> for $ListBuilder {
743        type Error = $crate::MultilineListBuilderError<<$EntryBuilder as std::str::FromStr>::Err>;
744
745        fn try_from(mllb: $crate::MultilineListBuilder<$EntryBuilder>)
746                    -> std::result::Result<$ListBuilder, Self::Error> {
747            Ok($ListBuilder { $things: mllb.try_into()? })
748        }
749    }
750
751    impl From<$ListBuilder> for MultilineListBuilder<$EntryBuilder> {
752        fn from(lb: $ListBuilder) -> MultilineListBuilder<$EntryBuilder> {
753            lb.$things.into()
754        }
755    }
756} }
757
758#[cfg(test)]
759mod test {
760    // @@ begin test lint list maintained by maint/add_warning @@
761    #![allow(clippy::bool_assert_comparison)]
762    #![allow(clippy::clone_on_copy)]
763    #![allow(clippy::dbg_macro)]
764    #![allow(clippy::mixed_attributes_style)]
765    #![allow(clippy::print_stderr)]
766    #![allow(clippy::print_stdout)]
767    #![allow(clippy::single_char_pattern)]
768    #![allow(clippy::unwrap_used)]
769    #![allow(clippy::unchecked_time_subtraction)]
770    #![allow(clippy::useless_vec)]
771    #![allow(clippy::needless_pass_by_value)]
772    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
773    use super::*;
774    use derive_builder::Builder;
775
776    #[derive(Eq, PartialEq, Builder)]
777    #[builder(derive(Deserialize))]
778    struct Outer {
779        #[builder(sub_builder, setter(custom))]
780        list: List,
781    }
782
783    define_list_builder_accessors! {
784        struct OuterBuilder {
785            list: [char],
786        }
787    }
788
789    type List = Vec<char>;
790
791    define_list_builder_helper! {
792        struct ListBuilder {
793            list: [char],
794        }
795        built: List = list;
796        default = vec!['a'];
797        item_build: |&c| Ok(c);
798    }
799
800    #[test]
801    fn nonempty_default() {
802        let mut b = OuterBuilder::default();
803        assert!(b.opt_list().is_none());
804        assert_eq! { b.build().expect("build failed").list, ['a'] };
805
806        b.list().push('b');
807        assert!(b.opt_list().is_some());
808        assert_eq! { b.build().expect("build failed").list, ['a', 'b'] };
809
810        for mut b in [b.clone(), OuterBuilder::default()] {
811            b.set_list(vec!['x', 'y']);
812            assert!(b.opt_list().is_some());
813            assert_eq! { b.build().expect("build failed").list, ['x', 'y'] };
814        }
815
816        *b.opt_list_mut() = None;
817        assert_eq! { b.build().expect("build failed").list, ['a'] };
818    }
819
820    #[test]
821    fn vecbuilder() {
822        // Minimal test, since rustdoc tests seem not to be finding the documentation inside
823        // the declaration of VecBuilder.  (Or at least that's what the coverage says.)
824        let mut b = VecBuilder::<u32>::default();
825        b.access().push(1);
826        b.access().push(2);
827        b.access().push(3);
828        assert_eq!(b.build().unwrap(), vec![1, 2, 3]);
829    }
830
831    #[test]
832    fn deser() {
833        let o: OuterBuilder = toml::from_str("list = ['x','y']").unwrap();
834        let o = o.build().unwrap();
835        assert_eq!(o.list, ['x', 'y']);
836
837        #[derive(Deserialize, Debug)]
838        struct OuterWithMllb {
839            #[serde(default)]
840            list: MultilineListBuilder<u32>,
841        }
842
843        let parse_ok = |s: &str| {
844            let o: OuterWithMllb = toml::from_str(s).unwrap();
845            let l: Option<Vec<_>> = o.list.try_into().unwrap();
846            l
847        };
848
849        let l = parse_ok("");
850        assert!(l.is_none());
851
852        let l = parse_ok("list = []");
853        assert!(l.unwrap().is_empty());
854
855        let l = parse_ok("list = [12,42]");
856        assert_eq!(l.unwrap(), [12, 42]);
857
858        let l = parse_ok(r#"list = """#);
859        assert!(l.unwrap().is_empty());
860
861        let l = parse_ok("list = \"\"\"\n12\n42\n\"\"\"\n");
862        assert_eq!(l.unwrap(), [12, 42]);
863
864        let e = toml::from_str::<OuterWithMllb>("list = [\"fail\"]")
865            .unwrap_err()
866            .to_string();
867        assert!(dbg!(e).contains(r#"invalid type: string "fail", expected u32"#));
868
869        let o = toml::from_str::<OuterWithMllb>("list = \"\"\"\nfail\n\"\"\"").unwrap();
870        let l: Result<Option<Vec<_>>, _> = o.list.try_into();
871        let e = l.unwrap_err().to_string();
872        assert_eq!(
873            e,
874            "multi-line string, line/item 1: could not parse \"fail\": invalid digit found in string"
875        );
876    }
877}