Expand description

Lists in builders

Use define_list_builder_helper and define_list_builder_accessors together when a configuration (or other struct with a builder) wants to contain a Vec of config sub-entries.

How to use these macros

  • For each kind of list, define a ThingList type alias for the validated form, and call define_list_builder_helper to define a ThingListBuilder helper type. (Different lists with the same Rust type, but which ought to have a different default, are different “kinds” and should each have a separately named type alias.)

  • For each struct field containing a list, in a struct deriving Builder, decorate the field with #[builder(sub_builder, setter(custom))] to (i) get derive_builder call the appropriate build method, (ii) suppress the derive_builder-generated setter.

  • For each struct containing lists, call define_list_builder_accessors to define the accessor methods.

Example - list of structs with builders

use derive_builder::Builder;
use serde::{Deserialize, Serialize};
use tor_config::{define_list_builder_helper, define_list_builder_accessors, ConfigBuildError};

#[derive(Builder, Debug, Eq, PartialEq)]
#[builder(build_fn(error = "ConfigBuildError"))]
#[builder(derive(Debug, Serialize, Deserialize))]
pub struct Thing { value: i32 }

#[derive(Builder, Debug, Eq, PartialEq)]
#[builder(build_fn(error = "ConfigBuildError"))]
#[builder(derive(Debug, Serialize, Deserialize))]
pub struct Outer {
    /// List of things, being built as part of the configuration
    #[builder(sub_builder, setter(custom))]
    things: ThingList,
}

define_list_builder_accessors! {
    struct OuterBuilder {
        pub things: [ThingBuilder],
    }
}

/// Type alias for use by list builder macrology
type ThingList = Vec<Thing>;

define_list_builder_helper! {
    pub(crate) struct ThingListBuilder {
        pub(crate) things: [ThingBuilder],
    }
    built: ThingList = things;
    default = vec![];
}

let mut builder = OuterBuilder::default();
builder.things().push(ThingBuilder::default().value(42).clone());
assert_eq!{ builder.build().unwrap().things, &[Thing { value: 42 }] }

builder.set_things(vec![ThingBuilder::default().value(38).clone()]);
assert_eq!{ builder.build().unwrap().things, &[Thing { value: 38 }] }

Example - list of trivial values

use derive_builder::Builder;
use serde::{Deserialize, Serialize};
use tor_config::{define_list_builder_helper, define_list_builder_accessors, ConfigBuildError};

#[derive(Builder, Debug, Eq, PartialEq)]
#[builder(build_fn(error = "ConfigBuildError"))]
#[builder(derive(Debug, Serialize, Deserialize))]
pub struct Outer {
    /// List of values, being built as part of the configuration
    #[builder(sub_builder, setter(custom))]
    values: ValueList,
}

define_list_builder_accessors! {
   struct OuterBuilder {
       pub values: [u32],
   }
}

/// Type alias for use by list builder macrology
pub type ValueList = Vec<u32>;

define_list_builder_helper! {
   pub(crate) struct ValueListBuilder {
       pub(crate) values: [u32],
   }
   built: ValueList = values;
   default = vec![27];
   item_build: |&value| Ok(value);
}

let mut builder = OuterBuilder::default();
assert_eq!{ builder.build().unwrap().values, &[27] }

builder.values().push(12);
assert_eq!{ builder.build().unwrap().values, &[27, 12] }

Structs

List of T, a straightforward type, being built as part of the configuration