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($crate::deps::educe::Educe, Clone, Debug)]
229 #[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 $vis fn build(&self) -> Result<$Built, $crate::ConfigBuildError> {
254 let default_buffer;
255 let $things = match &self.$things {
256 Some($things) => $things,
257 None => {
258 default_buffer = Self::default_list();
259 &default_buffer
260 }
261 };
262
263 let $things = $things
264 .iter()
265 .map(
266 $crate::deps::macro_first_nonempty!{
267 [ $( $item_build )? ],
268 [ |item| item.build() ],
269 }
270 )
271 .collect::<Result<_, $crate::ConfigBuildError>>()?;
272 Ok($built)
273 }
274
275 /// The default list
276 fn default_list() -> Vec<$EntryBuilder> {
277 $default
278 }
279
280 /// Resolve the list to the default if necessary and then return `&mut Vec`
281 $vis fn access(&mut self) -> &mut Vec<$EntryBuilder> {
282 self.$things.get_or_insert_with(Self::default_list)
283 }
284
285 /// Resolve the list to the default if necessary and then return `&mut Vec`
286 $vis fn access_opt(&self) -> &Option<Vec<$EntryBuilder>> {
287 &self.$things
288 }
289
290 /// Resolve the list to the default if necessary and then return `&mut Vec`
291 $vis fn access_opt_mut(&mut self) -> &mut Option<Vec<$EntryBuilder>> {
292 &mut self.$things
293 }
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]
351macro_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 $vis fn $things(&mut self) -> &mut Vec<$EntryBuilder> {
366 #[allow(unused_imports)]
367 use $crate::list_builder::DirectDefaultEmptyListBuilderAccessors as _;
368 self.$things.access()
369 }
370
371 /// Set the whole list (overriding the default)
372 $vis fn [<set_ $things>](&mut self, list: Vec<$EntryBuilder>) {
373 #[allow(unused_imports)]
374 use $crate::list_builder::DirectDefaultEmptyListBuilderAccessors as _;
375 *self.$things.access_opt_mut() = Some(list)
376 }
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 $vis fn [<opt_ $things>](&self) -> &Option<Vec<$EntryBuilder>> {
382 #[allow(unused_imports)]
383 use $crate::list_builder::DirectDefaultEmptyListBuilderAccessors as _;
384 self.$things.access_opt()
385 }
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 $vis fn [<opt_ $things _mut>](&mut self) -> &mut Option<Vec<$EntryBuilder>> {
391 #[allow(unused_imports)]
392 use $crate::list_builder::DirectDefaultEmptyListBuilderAccessors as _;
393 self.$things.access_opt_mut()
394 }
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.
428pub 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}
438impl<T> DirectDefaultEmptyListBuilderAccessors for Option<Vec<T>> {
439 type T = T;
440 fn access(&mut self) -> &mut Vec<T> {
441 self.get_or_insert_with(Vec::new)
442 }
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
451define_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 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]
576pub 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]
594pub 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.
614impl<'de, EB: Deserialize<'de>> Deserialize<'de> for MultilineListBuilder<EB> {
615 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
616 where
617 D: Deserializer<'de>,
618 {
619 deserializer.deserialize_any(MllbVisitor::default())
620 }
621}
622
623/// Visitor for deserialize_any for [`MultilineListBuilder`]
624#[derive(Educe)]
625#[educe(Default)]
626struct MllbVisitor<EB> {
627 /// Variance: this visitor constructs `EB`s
628 ret: PhantomData<fn() -> EB>,
629}
630
631impl<'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 fn visit_seq<A: serde::de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
639 let mut v = vec![];
640 while let Some(e) = seq.next_element()? {
641 v.push(e);
642 }
643 Ok(MultilineListBuilder::List(v))
644 }
645
646 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
647 self.visit_string(v.to_owned())
648 }
649 fn visit_string<E: serde::de::Error>(self, v: String) -> Result<Self::Value, E> {
650 Ok(MultilineListBuilder::String(v))
651 }
652
653 fn visit_none<E: serde::de::Error>(self) -> Result<Self::Value, E> {
654 Ok(MultilineListBuilder::Unspecified)
655 }
656}
657
658impl<EB> From<Option<Vec<EB>>> for MultilineListBuilder<EB> {
659 fn from(list: Option<Vec<EB>>) -> Self {
660 use MultilineListBuilder as MlLB;
661 match list {
662 None => MlLB::Unspecified,
663 Some(list) => MlLB::List(list),
664 }
665 }
666}
667
668impl<EB> TryInto<Option<Vec<EB>>> for MultilineListBuilder<EB>
669where
670 EB: FromStr,
671 EB::Err: std::error::Error + Clone + Send + Sync,
672{
673 type Error = MultilineListBuilderError<EB::Err>;
674 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 fn parse_collect<'s, I>(
679 iter: impl Iterator<Item = (usize, &'s str)>,
680 ) -> Result<Option<Vec<I>>, MultilineListBuilderError<I::Err>>
681 where
682 I: FromStr,
683 I::Err: std::error::Error + Clone + Send + Sync,
684 {
685 Ok(Some(
686 iter.map(|(i, l)| {
687 l.parse().map_err(|error| MultilineListBuilderError {
688 item_number: i + 1,
689 line: l.to_owned(),
690 error,
691 })
692 })
693 .try_collect()?,
694 ))
695 }
696
697 Ok(match self {
698 MlLB::Unspecified => None,
699 MlLB::List(list) => Some(list),
700 MlLB::String(s) => parse_collect(
701 s.lines()
702 .enumerate()
703 .map(|(i, l)| (i, l.trim()))
704 .filter(|(_, l)| !(l.starts_with('#') || l.is_empty())),
705 )?,
706 })
707 }
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]
718macro_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 fn try_from(mllb: $crate::MultilineListBuilder<$EntryBuilder>)
725 -> std::result::Result<$ListBuilder, Self::Error> {
726 Ok($ListBuilder { $things: mllb.try_into()? })
727 }
728 }
729
730 impl From<$ListBuilder> for MultilineListBuilder<$EntryBuilder> {
731 fn from(lb: $ListBuilder) -> MultilineListBuilder<$EntryBuilder> {
732 lb.$things.into()
733 }
734 }
735} }
736
737#[cfg(test)]
738mod 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}