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