Change builder list API

The new API is (roughly) as discussed in
  https://gitlab.torproject.org/tpo/core/arti/-/issues/451

This is quite a large commit and it is not convenient to split it up.
It contains the following changes:

 * Redo the list builder and accessor macros implemnetation,
   including docs and tests.

 * Change uses of define_list_config_builder.  In each case:
   - Move the docs about the default value to the containing field.
   - Remove the other docs (which were just recapitulations, and
     are now not needed since the ListBuilder is no longer public).
   - Rewmove or replace `pub` in the define_list_builder_helper call,
     so that the builder is no longer public.
   - Change the main macro call site to use define_list_builder_helper.
   - Add a call to define_list_builder_accessors.

 * Make the module `list_builder` pub so that we have somewhere to
   put the overview documentation.

 * Consequential changes:
   - Change `outer.inner().replace(X)` to `outer.set_inner(X)`
   - Consequential changes to imports (`use` statements).
This commit is contained in:
Ian Jackson 2022-05-04 13:32:35 +01:00
parent 71911d2921
commit 4bca912715
12 changed files with 347 additions and 178 deletions

View File

@ -396,8 +396,8 @@ mod test {
.clone();
let mut bld = TorClientConfig::builder();
bld.tor_network().authorities().replace(vec![auth]);
bld.tor_network().fallback_caches().replace(vec![fallback]);
bld.tor_network().set_authorities(vec![auth]);
bld.tor_network().set_fallback_caches(vec![fallback]);
bld.storage()
.cache_dir(CfgPath::new("/var/tmp/foo".to_owned()))
.state_dir(CfgPath::new("/var/tmp/bar".to_owned()));

View File

@ -235,11 +235,8 @@ mod test {
let mut bld = ArtiConfig::builder();
bld.proxy().socks_port(Some(9999));
bld.logging().console("warn");
bld.tor().tor_network().authorities().replace(vec![auth]);
bld.tor()
.tor_network()
.fallback_caches()
.replace(vec![fallback]);
bld.tor().tor_network().set_authorities(vec![auth]);
bld.tor().tor_network().set_fallback_caches(vec![fallback]);
bld.tor()
.storage()
.cache_dir(CfgPath::new("/var/tmp/foo".to_owned()))
@ -272,8 +269,7 @@ mod test {
bld.tor().preemptive_circuits().disable_at_threshold(12);
bld.tor()
.preemptive_circuits()
.initial_predicted_ports()
.replace(vec![80, 443]);
.set_initial_predicted_ports(vec![80, 443]);
bld.tor()
.preemptive_circuits()
.prediction_lifetime(Duration::from_secs(3600))

View File

@ -5,7 +5,8 @@ use derive_builder::Builder;
use serde::Deserialize;
use std::path::Path;
use std::str::FromStr;
use tor_config::{define_list_config_builder, CfgPath, ConfigBuildError};
use tor_config::{define_list_builder_accessors, define_list_builder_helper};
use tor_config::{CfgPath, ConfigBuildError};
use tracing::Subscriber;
use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::layer::SubscriberExt;
@ -37,9 +38,11 @@ pub struct LoggingConfig {
journald: Option<String>,
/// Configuration for one or more logfiles.
///
/// The default is not to log to any files.
#[serde(default)]
#[builder_field_attr(serde(default))]
#[builder(sub_builder)]
#[builder(sub_builder, setter(custom))]
files: LogfileListConfig,
}
@ -65,17 +68,20 @@ impl LoggingConfig {
/// Local type alias, mostly helpful for derive_builder to DTRT
type LogfileListConfig = Vec<LogfileConfig>;
define_list_config_builder! {
/// List of logfiles to use, being built as part of the configuration.
///
/// The default is not to log to any files.
pub struct LogfileListConfigBuilder {
define_list_builder_helper! {
struct LogfileListConfigBuilder {
files: [LogfileConfigBuilder],
}
built: LogfileListConfig = files;
default = vec![];
}
define_list_builder_accessors! {
struct LoggingConfigBuilder {
pub files: [LogfileConfigBuilder],
}
}
/// Configuration information for an (optionally rotating) logfile.
#[derive(Deserialize, Debug, Builder, Clone, Eq, PartialEq)]
#[builder(derive(Deserialize))]

View File

@ -5,7 +5,7 @@
//! Most types in this module are re-exported by `arti-client`.
use tor_basic_utils::define_accessor_trait;
use tor_config::{define_list_config_builder, ConfigBuildError};
use tor_config::{define_list_builder_accessors, define_list_builder_helper, ConfigBuildError};
use tor_guardmgr::fallback::FallbackList;
use derive_builder::Builder;
@ -113,7 +113,9 @@ pub struct PreemptiveCircuitConfig {
///
/// This value cannot be changed on a running Arti client, because doing so
/// would be meaningless.
#[builder(sub_builder)]
///
/// The default is `[80, 443]`.
#[builder(sub_builder, setter(custom))]
pub(crate) initial_predicted_ports: PredictedPortsList,
/// After we see the client request a connection to a new port, how long
@ -185,11 +187,8 @@ fn default_preemptive_threshold() -> usize {
/// Built list of configured preemptive ports
type PredictedPortsList = Vec<u16>;
define_list_config_builder! {
/// List of preemptive ports, being built as part of the configuration.
///
/// The default is `[80, 443]`.
pub struct PredictedPortsListBuilder {
define_list_builder_helper! {
struct PredictedPortsListBuilder {
pub(crate) ports: [u16],
}
built: PredictedPortsList = ports;
@ -197,6 +196,12 @@ define_list_config_builder! {
item_build: |&port| Ok(port);
}
define_list_builder_accessors! {
struct PreemptiveCircuitConfigBuilder {
pub initial_predicted_ports: [u16],
}
}
/// Return default target ports
fn default_preemptive_ports() -> Vec<u16> {
vec![80, 443]

View File

@ -95,7 +95,7 @@ mod test {
#[test]
fn predicts_starting_ports() {
let mut cfg = PreemptiveCircuitConfig::builder();
cfg.initial_predicted_ports().replace(vec![]);
cfg.set_initial_predicted_ports(vec![]);
cfg.prediction_lifetime(Duration::from_secs(2));
let predictor = PreemptiveCircuitPredictor::new(cfg.build().unwrap());
@ -108,7 +108,7 @@ mod test {
);
let mut cfg = PreemptiveCircuitConfig::builder();
cfg.initial_predicted_ports().replace(vec![80]);
cfg.set_initial_predicted_ports(vec![80]);
cfg.prediction_lifetime(Duration::from_secs(2));
let predictor = PreemptiveCircuitPredictor::new(cfg.build().unwrap());
@ -131,7 +131,7 @@ mod test {
#[test]
fn predicts_used_ports() {
let mut cfg = PreemptiveCircuitConfig::builder();
cfg.initial_predicted_ports().replace(vec![]);
cfg.set_initial_predicted_ports(vec![]);
cfg.prediction_lifetime(Duration::from_secs(2));
let mut predictor = PreemptiveCircuitPredictor::new(cfg.build().unwrap());
@ -164,7 +164,7 @@ mod test {
#[test]
fn does_not_predict_old_ports() {
let mut cfg = PreemptiveCircuitConfig::builder();
cfg.initial_predicted_ports().replace(vec![]);
cfg.set_initial_predicted_ports(vec![]);
cfg.prediction_lifetime(Duration::from_secs(2));
let mut predictor = PreemptiveCircuitPredictor::new(cfg.build().unwrap());
let more_than_an_hour_ago = Instant::now() - Duration::from_secs(60 * 60 + 1);

View File

@ -47,7 +47,7 @@
#![deny(clippy::unwrap_used)]
mod err;
mod list_builder;
pub mod list_builder;
mod mut_cfg;
mod path;

View File

@ -1,86 +1,179 @@
//! Config list builder for lists
//! 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 differnet "kinds" and should each have a separately named type alias.)
//!
// An alternative design would be declare the field on `Outer` as `Vec<Thing>`, and to provide
// a `VecBuilder`. But:
//
// (i) the `.build()` method would have to be from a trait (because it would be `VecBuilder<Item>`
// which would have to contain some `ItemBuilder`, and for the benefit of `VecBuilder::build()`).
// Although derive_builder` does not provide that trait now, this problem is not insuperable,
// but it would mean us inventing a `Buildable` trait and a macro to generate it, or forking
// derive_builder further.
//
// (ii) `VecBuilder<Item>::build()` would have to have the same default list for every
// type Item (an empty list). So places where the default list is not empty would need special
// handling. The special handling would look quite like what we have here.
//
//! * 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.
//!
// `ThingLisgtBuiler` exixsts for two reasons:
//
// * derive_builder wants to call simply `build` on the builder struct field, and will
// generate code for attaching the field name to any error which occurs. We could
// override the per-field build expression, but it would be quite a lot fo typing and
// would recapitulate the field name three times.
//
// * The field accessors (which must be generated by a different macro_rules macros, at least
// unless we soup up derive_builder some more) might need to do defaulting, too. if
// the builder field is its own type, that can be a method on that type.
//
//! * 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;
//! use tor_config::{define_list_builder_helper, define_list_builder_accessors, ConfigBuildError};
//!
//! #[derive(Builder, Debug, Eq, PartialEq)]
//! #[builder(build_fn(error = "ConfigBuildError"))]
//! #[builder(derive(Deserialize))]
//! pub struct Thing { value: i32 }
//!
//! #[derive(Builder, Debug, Eq, PartialEq)]
//! #[builder(build_fn(error = "ConfigBuildError"))]
//! #[builder(derive(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;
//! use tor_config::{define_list_builder_helper, define_list_builder_accessors, ConfigBuildError};
//!
//! #[derive(Builder, Debug, Eq, PartialEq)]
//! #[builder(build_fn(error = "ConfigBuildError"))]
//! #[builder(derive(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] }
//! ```
/// Define a list builder struct and implement the conventional methods
/// Define a list builder struct for use with [`define_list_builder_accessors`]
///
/// The macro-generated builder struct contains `Option<Vec<ThingBuilder>>`, to allow it to
/// Generates an builder struct that can be used with derive_builder
/// and [`define_list_builder_accessors`] to configure a list of some kind.
///
/// **See the [`list_builder` module documentation](crate::list_builder) for an overview.**
///
/// ### Generated struct
///
/// This macro-generated builder struct contains `Option<Vec<ThingBuilder>>`, to allow it to
/// distinguish "never set" from "has been adjusted or set, possibly to the empty list".
///
/// `#[derive(Default, Clone, Deserialize)]` will be applied, but you can specify other attributes
/// too. You should supply a doc comment for the builder struct, as shown in the example.
/// The doc comment should state the default value.
/// This struct is not exposed as part of the API for setting the configuration.
/// Generally the visibility (`$vis`) should be private,
/// but sometimes `pub(crate)` or `pub` is necessary,
/// for example if the list is to be included in a struct in another module or crate.
/// Usually `$field_vis` should be the same as `$vis`.
///
/// `#[derive(Default, Clone, Deserialize)]` will be applied to the generated builder,
/// but you can specify other attributes too.
/// There is no need to supply any documentation; this is an internal struct and
/// the macro will supply a suitable (generic) doc comment.
/// Documentation for the semantics and default value should be applied
/// to the field(s) in the containing struct(s).
///
/// `#[serde(transparent)]` will be applied to the generated `ThingBuilder` struct,
/// so that it deserializes just like `Option<Vec<Thing>>`.
///
/// ### Input to the macro
///
/// For the input syntax, refer to the docs autogenerated from the macro's matcher.
///
/// The `built` clause specifies the type of the built value, and how to construct it.
/// In the expression part, `things` (the field name) will be the resolved `Vec<Thing>`
/// and should be consumed by the expression.
/// If the built value is simply a `Vec`, you can just write `built: things;`.
/// In the expression part, `things` (the field name) will be the default-resolved `Vec<Thing>`;
/// it should be consumed by the expression.
/// If the built value is simply a `Vec`, you can just write `built: ThingList = things;`.
///
/// The `default` clause must provide an expression evaluating to a `Vec<ThingBuilder>`.
///
/// The `item_build` clause, if supplied, provides a closure with type
/// `FnMut(&ThingBuilder) -> Result<Thing, ConfigBuildErro>`; the default is to call
/// `thing_builder.build()`.
///
/// ### Example - list of structs with builders
///
/// ```
/// use derive_builder::Builder;
/// use serde::Deserialize;
/// use tor_config::{define_list_config_builder, ConfigBuildError};
///
/// #[derive(Builder, Debug, Eq, PartialEq)]
/// #[builder(build_fn(error = "ConfigBuildError"))]
/// #[builder(derive(Deserialize))]
/// pub struct Thing { value: i32 }
///
/// #[derive(Debug)]
/// pub struct ThingList { things: Vec<Thing> }
///
/// define_list_config_builder! {
/// /// List of things, being built as part of the configuration
/// pub struct ThingListBuilder {
/// pub(crate) things: [ThingBuilder],
/// }
/// built: ThingList = ThingList { things };
/// default = vec![];
/// }
///
/// let mut thinglist = ThingListBuilder::default();
/// thinglist.append(ThingBuilder::default().value(42).clone());
/// assert_eq!{ thinglist.build().unwrap().things, &[Thing { value: 42 }] }
///
/// thinglist.replace(vec![ThingBuilder::default().value(38).clone()]);
/// assert_eq!{ thinglist.build().unwrap().things, &[Thing { value: 38 }] }
/// ```
///
/// ### Example - list of trivial values
///
/// ```
/// use derive_builder::Builder;
/// use serde::Deserialize;
/// use tor_config::{define_list_config_builder, ConfigBuildError};
///
/// #[derive(Debug)]
/// pub struct ValueList { values: Vec<u32> }
///
/// define_list_config_builder! {
/// /// List of values, being built as part of the configuration
/// pub struct ValueListBuilder {
/// pub(crate) values: [u32],
/// }
/// built: ValueList = ValueList { values };
/// default = vec![27];
/// item_build: |&value| Ok(value);
/// }
///
/// let mut valuelist = ValueListBuilder::default();
/// assert_eq!{ valuelist.build().unwrap().values, &[27] }
///
/// valuelist.append(12);
/// assert_eq!{ valuelist.build().unwrap().values, &[27, 12] }
/// ```
/// `FnMut(&ThingBuilder) -> Result<Thing, ConfigBuildError>`;
/// the default is to call `thing_builder.build()`.
#[macro_export]
macro_rules! define_list_config_builder {
macro_rules! define_list_builder_helper {
{
$(#[ $docs_and_attrs:meta ])*
$vis:vis struct $ListBuilder:ident {
@ -90,43 +183,18 @@ macro_rules! define_list_config_builder {
default = $default:expr;
$( item_build: $item_build:expr; )?
} => {
$(#[ $docs_and_attrs ])*
#[derive(Default, Clone, $crate::serde::Deserialize)]
#[serde(transparent)]
/// Wrapper struct to help derive_builder find the right types and methods
///
/// This is a builder pattern struct which will be resolved
/// during configuration resolution, via the `build` method.
/// This struct is not part of the configuration API.
/// Refer to the containing structures for information on how to build the config.
$vis struct $ListBuilder {
/// The list, as overridden
$field_vis $things: Option<Vec<$EntryBuilder>>,
}
impl $ListBuilder {
/// Add one item to the end of the list.
///
/// If the list hasn't been set or adjusted yet, it is initialised to the default.
/// Then `item` is added.
$vis fn append(&mut self, item: $EntryBuilder) -> &mut Self {
self.$things
.get_or_insert_with(Self::default_list)
.push(item);
self
}
/// Set the list to the supplied one, discarding any previous settings.
///
/// After `replace` has been called, the default list will no longer be used.
$vis fn replace(&mut self, list: impl IntoIterator<Item = $EntryBuilder>) -> &mut Self {
self.$things = Some(list.into_iter().collect());
self
}
/// Checks whether any calls have been made to set or adjust the list.
///
/// If `append` or `replace` have been called, this will return `true`.
$vis fn is_unmodified_default(&self) -> bool {
self.$things.is_none()
}
/// Resolve this list to a list of built items.
///
/// If the value is still the [`Default`],
@ -159,37 +227,131 @@ macro_rules! define_list_config_builder {
fn default_list() -> Vec<$EntryBuilder> {
$default
}
/// Resolve the list to the default if necessary and then return `&mut Vec`
$vis fn access(&mut self) -> &mut Vec<$EntryBuilder> {
self.$things.get_or_insert_with(Self::default_list)
}
/// Resolve the list to the default if necessary and then return `&mut Vec`
$vis fn access_opt(&self) -> &Option<Vec<$EntryBuilder>> {
&self.$things
}
/// Resolve the list to the default if necessary and then return `&mut Vec`
$vis fn access_opt_mut(&mut self) -> &mut Option<Vec<$EntryBuilder>> {
&mut self.$things
}
}
}
}
/// Define accessor methods for a configuration item which is a list
///
/// **See the [`list_builder` module documentation](crate::list_builder) for an overview.**
///
/// Generates the following methods for each specified field:
///
/// ```skip
/// impl $OuterBuilder {
/// pub fn $things(&mut self) -> &mut Vec<$EntryBuilder> { .. }
/// pub fn set_$things(&mut self, list: Vec<$EntryBuilder>) { .. }
/// pub fn opt_$things(&self) -> &Option<Vec<$EntryBuilder>> { .. }
/// pub fn opt_$things_mut>](&mut self) -> &mut Option<Vec<$EntryBuilder>> { .. }
/// }
/// ```
///
/// Each `$EntryBuilder` should have been defined by [`define_list_builder_helper`];
/// the method bodies from this macro rely on facilities which will beprovided by that macro.
///
/// You can call `define_list_builder_accessors` once for a particular `$OuterBuilder`,
/// with any number of fields with possibly different entry (`$EntryBuilder`) types.
#[macro_export]
macro_rules! define_list_builder_accessors {
{
struct $OuterBuilder:ty {
$(
$vis:vis $things:ident: [$EntryBuilder:ty],
)*
}
} => {
impl $OuterBuilder { $( $crate::paste!{
/// Access the being-built list (resolving default)
///
/// If the field has not yet been set or accessed, the default list will be
/// constructed and a mutable reference to the now-defaulted list of builders
/// will be returend.
$vis fn $things(&mut self) -> &mut Vec<$EntryBuilder> {
self.$things.access()
}
/// Set the whole list (overriding the default)
$vis fn [<set_ $things>](&mut self, list: Vec<$EntryBuilder>) {
*self.$things.access_opt_mut() = Some(list)
}
/// Inspect the being-built list (iwth default unresolved)
///
/// If the list has not yet been set, or accessed, `&None` is returned.
$vis fn [<opt_ $things>](&self) -> &Option<Vec<$EntryBuilder>> {
self.$things.access_opt()
}
/// Mutably access the being-built list (iwth default unresolved)
///
/// If the list has not yet been set, or accessed, `&mut None` is returned.
$vis fn [<opt_ $things _mut>](&mut self) -> &mut Option<Vec<$EntryBuilder>> {
self.$things.access_opt_mut()
}
} )* }
}
}
#[cfg(test)]
mod test {
use derive_builder::Builder;
use serde::Deserialize;
#[test]
fn nonempty_default() {
define_list_config_builder! {
struct ListBuilder {
chars: [char],
#[derive(Eq, PartialEq, Builder, Deserialize)]
struct Outer {
#[builder(sub_builder, setter(custom))]
list: List,
}
define_list_builder_accessors! {
struct OuterBuilder {
list: [char],
}
built: List = chars;
default = vec!['a'];
item_build: |&c| Ok(c);
}
type List = Vec<char>;
let mut b = ListBuilder::default();
assert!(b.is_unmodified_default());
assert_eq! { (&b).build().expect("build failed"), ['a'] };
b.append('b');
assert!(!b.is_unmodified_default());
assert_eq! { (&b).build().expect("build failed"), ['a', 'b'] };
for mut b in IntoIterator::into_iter([b, ListBuilder::default()]) {
b.replace(vec!['x', 'y']);
assert!(!b.is_unmodified_default());
assert_eq! { (&b).build().expect("build failed"), ['x', 'y'] };
define_list_builder_helper! {
struct ListBuilder {
list: [char],
}
built: List = list;
default = vec!['a'];
item_build: |&c| Ok(c);
}
let mut b = OuterBuilder::default();
assert!(b.opt_list().is_none());
assert_eq! { (&b).build().expect("build failed").list, ['a'] };
b.list().push('b');
assert!(b.opt_list().is_some());
assert_eq! { (&b).build().expect("build failed").list, ['a', 'b'] };
for mut b in [b.clone(), OuterBuilder::default()] {
b.set_list(vec!['x', 'y']);
assert!(b.opt_list().is_some());
assert_eq! { (&b).build().expect("build failed").list, ['x', 'y'] };
}
*b.opt_list_mut() = None;
assert_eq! { (&b).build().expect("build failed").list, ['a'] };
}
}

View File

@ -5,7 +5,7 @@
use derive_builder::Builder;
use serde::Deserialize;
use tor_config::{define_list_config_builder, ConfigBuildError};
use tor_config::{define_list_builder_helper, ConfigBuildError};
use tor_llcrypto::pk::rsa::RsaIdentity;
/// A single authority that signs a consensus directory.
@ -44,12 +44,8 @@ impl Authority {
/// Authority list, built
pub(crate) type AuthorityList = Vec<Authority>;
define_list_config_builder! {
/// List of authorities, being built as part of the configuration.
///
/// The default is to use a set of compiled-in authorities,
/// whose identities and public keys are shipped as part of the Arti source code.
pub struct AuthorityListBuilder {
define_list_builder_helper! {
pub(crate) struct AuthorityListBuilder {
authorities: [AuthorityBuilder],
}
built: AuthorityList = authorities;

View File

@ -8,11 +8,12 @@
//! The types in this module are re-exported from `arti-client`: any changes
//! here must be reflected in the version of `arti-client`.
use crate::authority::{Authority, AuthorityList};
use crate::authority::{Authority, AuthorityBuilder, AuthorityList, AuthorityListBuilder};
use crate::retry::{DownloadSchedule, DownloadScheduleBuilder};
use crate::storage::DynStore;
use crate::{AuthorityListBuilder, Result};
use tor_config::ConfigBuildError;
use crate::Result;
use tor_config::{define_list_builder_accessors, ConfigBuildError};
use tor_guardmgr::fallback::FallbackDirBuilder;
use tor_guardmgr::fallback::FallbackListBuilder;
use tor_netdoc::doc::netstatus;
@ -41,11 +42,11 @@ pub struct NetworkConfig {
///
/// This section can be changed in a running Arti client. Doing so will
/// affect future download attempts only.
#[builder(sub_builder)]
///
/// The default is to use a set of compiled-in fallback directories,
/// whose addresses and public keys are shipped as part of the Arti source code.
#[serde(default)]
#[serde(rename = "fallback_caches")]
#[builder_field_attr(serde(rename = "fallback_caches"))]
#[builder(setter(name = "fallback_caches"))]
#[builder(sub_builder, setter(custom))]
pub(crate) fallback_caches: tor_guardmgr::fallback::FallbackList,
/// List of directory authorities which we expect to sign consensus
@ -55,10 +56,20 @@ pub struct NetworkConfig {
/// with Arti.)
///
/// This section cannot be changed in a running Arti client.
#[builder(sub_builder)]
///
/// The default is to use a set of compiled-in authorities,
/// whose identities and public keys are shipped as part of the Arti source code.
#[builder(sub_builder, setter(custom))]
pub(crate) authorities: AuthorityList,
}
define_list_builder_accessors! {
struct NetworkConfigBuilder {
pub fallback_caches: [FallbackDirBuilder],
pub authorities: [AuthorityBuilder],
}
}
impl Default for NetworkConfig {
fn default() -> Self {
NetworkConfig {
@ -87,7 +98,7 @@ impl NetworkConfig {
impl NetworkConfigBuilder {
/// Check that this builder will give a reasonable network.
fn validate(&self) -> std::result::Result<(), ConfigBuildError> {
if !self.authorities.is_unmodified_default() && self.fallback_caches.is_unmodified_default() {
if self.opt_authorities().is_some() && self.opt_fallback_caches().is_none() {
return Err(ConfigBuildError::Inconsistent {
fields: vec!["authorities".to_owned(), "fallbacks".to_owned()],
problem: "Non-default authorities are use, but the fallback list is not overridden"
@ -310,7 +321,7 @@ mod test {
// with any authorities set, the fallback list _must_ be set
// or the build fails.
bld.authorities().replace(vec![
bld.set_authorities(vec![
Authority::builder()
.name("Hello")
.v3ident([b'?'; 20].into())
@ -322,7 +333,7 @@ mod test {
]);
assert!(bld.build().is_err());
bld.fallback_caches().replace(vec![FallbackDir::builder()
bld.set_fallback_caches(vec![FallbackDir::builder()
.rsa_identity([b'x'; 20].into())
.ed_identity([b'y'; 32].into())
.orport("127.0.0.1:99".parse().unwrap())

View File

@ -92,7 +92,7 @@ use std::time::Duration;
use std::{collections::HashMap, sync::Weak};
use std::{fmt::Debug, time::SystemTime};
pub use authority::{Authority, AuthorityBuilder, AuthorityListBuilder};
pub use authority::{Authority, AuthorityBuilder};
pub use config::{
DirMgrConfig, DownloadScheduleConfig, DownloadScheduleConfigBuilder, NetworkConfig,
NetworkConfigBuilder,

View File

@ -1011,9 +1011,9 @@ mod test {
impl DirRcv {
fn new(now: SystemTime, authorities: Option<Vec<AuthorityBuilder>>) -> Self {
let mut netcfg = crate::NetworkConfig::builder();
netcfg.fallback_caches().replace(vec![]);
netcfg.set_fallback_caches(vec![]);
if let Some(a) = authorities {
netcfg.authorities().replace(a);
netcfg.set_authorities(a);
}
let cfg = DirMgrConfig {
cache_path: "/we_will_never_use_this/".into(),

View File

@ -8,7 +8,7 @@ use super::{DirStatus, FallbackDir, FallbackDirBuilder};
use crate::fallback::default_fallbacks;
use crate::{ids::FallbackId, PickGuardError};
use serde::Deserialize;
use tor_config::define_list_config_builder;
use tor_config::define_list_builder_helper;
/// A list of fallback directories.
///
@ -30,15 +30,8 @@ impl<T: IntoIterator<Item = FallbackDir>> From<T> for FallbackList {
}
}
define_list_config_builder! {
/// List of fallback directories, being built as part of the configuration.
///
/// Fallback directories (represented by [`FallbackDir`]) are used by Tor
/// clients when they don't already have enough other directory information to
/// contact the network.
///
/// The default is to use a set of compiled-in fallback directories,
/// whose addresses and public keys are shipped as part of the Arti source code.
define_list_builder_helper! {
// pub because tor-dirmgr needs it for NetworkConfig.fallback_caches
pub struct FallbackListBuilder {
pub(crate) fallbacks: [FallbackDirBuilder],
}