Introduce define_list_config_builder macro
This replaces two almost-identical sets of structs and impls. More are on the way, as per https://gitlab.torproject.org/tpo/core/arti/-/issues/447
This commit is contained in:
parent
62aa071998
commit
0036b91662
|
@ -5,7 +5,7 @@ use derive_builder::Builder;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use tor_config::{CfgPath, ConfigBuildError};
|
use tor_config::{define_list_config_builder, CfgPath, ConfigBuildError};
|
||||||
use tracing::Subscriber;
|
use tracing::Subscriber;
|
||||||
use tracing_appender::non_blocking::WorkerGuard;
|
use tracing_appender::non_blocking::WorkerGuard;
|
||||||
use tracing_subscriber::layer::SubscriberExt;
|
use tracing_subscriber::layer::SubscriberExt;
|
||||||
|
@ -65,52 +65,15 @@ impl LoggingConfig {
|
||||||
/// Local type alias, mostly helpful for derive_builder to DTRT
|
/// Local type alias, mostly helpful for derive_builder to DTRT
|
||||||
type LogfileListConfig = Vec<LogfileConfig>;
|
type LogfileListConfig = Vec<LogfileConfig>;
|
||||||
|
|
||||||
#[derive(Default, Clone, Deserialize)]
|
define_list_config_builder! {
|
||||||
#[serde(transparent)]
|
[
|
||||||
/// List of logfiles to use, being built as part of the configuration
|
/// List of logfiles to use, being built as part of the configuration
|
||||||
pub struct LogfileListConfigBuilder {
|
]
|
||||||
/// The logfiles, as overridden
|
pub struct LogfileListConfigBuilder {
|
||||||
files: Option<Vec<LogfileConfigBuilder>>,
|
files: [LogfileConfigBuilder],
|
||||||
}
|
|
||||||
|
|
||||||
impl LogfileListConfigBuilder {
|
|
||||||
/// Add a file logger
|
|
||||||
pub fn append(&mut self, file: LogfileConfigBuilder) -> &mut Self {
|
|
||||||
self.files
|
|
||||||
.get_or_insert_with(Self::default_files)
|
|
||||||
.push(file);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the list of file loggers to the supplied `files`
|
|
||||||
pub fn set(&mut self, files: impl IntoIterator<Item = LogfileConfigBuilder>) -> &mut Self {
|
|
||||||
self.files = Some(files.into_iter().collect());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Default logfiles
|
|
||||||
///
|
|
||||||
/// (Currently) there are no defauolt logfiles.
|
|
||||||
pub(crate) fn default_files() -> Vec<LogfileConfigBuilder> {
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resolve `LoggingConfigBuilder.files` to a value for `LoggingConfig.files`
|
|
||||||
pub(crate) fn build(&self) -> Result<Vec<LogfileConfig>, ConfigBuildError> {
|
|
||||||
let default_buffer;
|
|
||||||
let files = match &self.files {
|
|
||||||
Some(files) => files,
|
|
||||||
None => {
|
|
||||||
default_buffer = Self::default_files();
|
|
||||||
&default_buffer
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let files = files
|
|
||||||
.iter()
|
|
||||||
.map(|item| item.build())
|
|
||||||
.collect::<Result<_, _>>()?;
|
|
||||||
Ok(files)
|
|
||||||
}
|
}
|
||||||
|
built: LogfileListConfig = files;
|
||||||
|
default = vec![];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configuration information for an (optionally rotating) logfile.
|
/// Configuration information for an (optionally rotating) logfile.
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
#![deny(clippy::unwrap_used)]
|
#![deny(clippy::unwrap_used)]
|
||||||
|
|
||||||
mod err;
|
mod err;
|
||||||
|
mod list_builder;
|
||||||
mod mut_cfg;
|
mod mut_cfg;
|
||||||
mod path;
|
mod path;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
//! Config list builder for lists
|
||||||
|
|
||||||
|
/// Define a list builder struct and implement the conventional methods
|
||||||
|
///
|
||||||
|
/// The 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 `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;`.
|
||||||
|
///
|
||||||
|
/// The `default` clause must provide an expression evaluating to a `Vec<ThingBuilder>`. If it is
|
||||||
|
/// nontrivial, you should put the actual defaulting functionality in a (probably-private)
|
||||||
|
/// function, as the macro will expand it twice.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// 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.set(vec![ThingBuilder::default().value(38).clone()]);
|
||||||
|
/// assert_eq!{ thinglist.build().unwrap().things, &[Thing { value: 38 }] }
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! define_list_config_builder {
|
||||||
|
{
|
||||||
|
[
|
||||||
|
$($docs_and_attrs:tt)*
|
||||||
|
]
|
||||||
|
pub struct $ListBuilder:ident {
|
||||||
|
$field_vis:vis $things:ident : [$EntryBuilder:ty] $(,)?
|
||||||
|
}
|
||||||
|
built: $Built:ty = $built:expr;
|
||||||
|
default = $default:expr;
|
||||||
|
} => {
|
||||||
|
$($docs_and_attrs)*
|
||||||
|
#[derive(Default, Clone, Deserialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
///
|
||||||
|
/// This is a builder pattern struct which will be resolved
|
||||||
|
/// during configuration resolution, via the `build` method.
|
||||||
|
pub struct $ListBuilder {
|
||||||
|
/// The list, as overridden
|
||||||
|
$field_vis $things: Option<Vec<$EntryBuilder>>,
|
||||||
|
}
|
||||||
|
impl $ListBuilder {
|
||||||
|
/// Add one
|
||||||
|
pub fn append(&mut self, item: $EntryBuilder) -> &mut Self {
|
||||||
|
self.$things
|
||||||
|
.get_or_insert_with(|| $default)
|
||||||
|
.push(item);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the list to the supplied one, discarding any previous settings
|
||||||
|
pub fn set(&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
|
||||||
|
pub fn is_unmodified_default(&self) -> bool {
|
||||||
|
self.$things.is_none()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve to a list of built items
|
||||||
|
pub fn build(&self) -> Result<$Built, ConfigBuildError> {
|
||||||
|
let default_buffer;
|
||||||
|
let $things = match &self.$things {
|
||||||
|
Some($things) => $things,
|
||||||
|
None => {
|
||||||
|
default_buffer = $default;
|
||||||
|
&default_buffer
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let $things = $things
|
||||||
|
.iter()
|
||||||
|
.map(|item| item.build())
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
Ok($built)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ use super::{DirStatus, FallbackDir, FallbackDirBuilder};
|
||||||
use crate::fallback::default_fallbacks;
|
use crate::fallback::default_fallbacks;
|
||||||
use crate::{ids::FallbackId, PickGuardError};
|
use crate::{ids::FallbackId, PickGuardError};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tor_config::ConfigBuildError;
|
use tor_config::{define_list_config_builder, ConfigBuildError};
|
||||||
|
|
||||||
/// A list of fallback directories.
|
/// A list of fallback directories.
|
||||||
///
|
///
|
||||||
|
@ -30,54 +30,19 @@ impl<T: IntoIterator<Item = FallbackDir>> From<T> for FallbackList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List of fallback directories, being built as part of the configuration
|
define_list_config_builder! {
|
||||||
///
|
[
|
||||||
/// Fallback directories (represented by [`FallbackDir`]) are used by Tor
|
/// List of fallback directories, being built as part of the configuration
|
||||||
/// clients when they don't already have enough other directory information to
|
///
|
||||||
/// contact the network.
|
/// Fallback directories (represented by [`FallbackDir`]) are used by Tor
|
||||||
///
|
/// clients when they don't already have enough other directory information to
|
||||||
/// This is a builder pattern struct which will be resolved into a `FallbackList`
|
/// contact the network.
|
||||||
/// during configuration resolution, via the [`build`](FallbackListBuilder::build) method.
|
]
|
||||||
#[derive(Default, Clone, Deserialize)]
|
pub struct FallbackListBuilder {
|
||||||
#[serde(transparent)]
|
pub(crate) fallbacks: [FallbackDirBuilder],
|
||||||
pub struct FallbackListBuilder {
|
|
||||||
/// The fallbacks, as overridden
|
|
||||||
pub(crate) fallbacks: Option<Vec<FallbackDirBuilder>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FallbackListBuilder {
|
|
||||||
/// Append `fallback` to the list
|
|
||||||
pub fn append(&mut self, fallback: FallbackDirBuilder) {
|
|
||||||
self.fallbacks
|
|
||||||
.get_or_insert_with(default_fallbacks)
|
|
||||||
.push(fallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Replace the list with the supplied one
|
|
||||||
pub fn set(&mut self, fallbacks: impl IntoIterator<Item = FallbackDirBuilder>) {
|
|
||||||
self.fallbacks = Some(fallbacks.into_iter().collect());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks whether any calls have been made to set or adjust the set
|
|
||||||
pub fn is_unmodified_default(&self) -> bool {
|
|
||||||
self.fallbacks.is_none()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resolve into a `FallbackList`
|
|
||||||
pub fn build(&self) -> Result<FallbackList, ConfigBuildError> {
|
|
||||||
let default_buffer;
|
|
||||||
let fallbacks = if let Some(f) = &self.fallbacks {
|
|
||||||
f
|
|
||||||
} else {
|
|
||||||
default_buffer = default_fallbacks();
|
|
||||||
&default_buffer
|
|
||||||
};
|
|
||||||
let fallbacks = fallbacks
|
|
||||||
.iter()
|
|
||||||
.map(|item| item.build())
|
|
||||||
.collect::<Result<_, _>>()?;
|
|
||||||
Ok(FallbackList { fallbacks })
|
|
||||||
}
|
}
|
||||||
|
built: FallbackList = FallbackList { fallbacks };
|
||||||
|
default = default_fallbacks();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FallbackList {
|
impl FallbackList {
|
||||||
|
|
Loading…
Reference in New Issue