aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2024-03-13 18:52:39 +0000
committerNick Mathewson <nickm@torproject.org>2024-03-13 18:52:39 +0000
commit6b120ec2117b8b0dfb5f0daa1746ec532cf3ad3a (patch)
tree29d14abc31afd46fe92fc1b869aa66dc1c94f988
parentdc3605c605b42a46450b39eec3f676a88cf4359f (diff)
parentdb912ae774dc378f78d7ad14b814f879c69a8f41 (diff)
downloadarti-6b120ec2117b8b0dfb5f0daa1746ec532cf3ad3a.tar.gz
arti-6b120ec2117b8b0dfb5f0daa1746ec532cf3ad3a.zip
Merge branch 'encapsulate_config_rs' into 'main'
Encapsulate usage of config-rs inside tor-config. See merge request tpo/core/arti!2040
-rw-r--r--Cargo.lock3
-rw-r--r--crates/arti-client/src/config.rs4
-rw-r--r--crates/arti-testing/Cargo.toml4
-rw-r--r--crates/arti/Cargo.toml1
-rw-r--r--crates/arti/src/cfg.rs26
-rw-r--r--crates/arti/src/reload_cfg.rs1
-rw-r--r--crates/tor-config/Cargo.toml1
-rw-r--r--crates/tor-config/README.md2
-rw-r--r--crates/tor-config/semver.md5
-rw-r--r--crates/tor-config/src/err.rs17
-rw-r--r--crates/tor-config/src/lib.rs39
-rw-r--r--crates/tor-config/src/list_builder.rs8
-rw-r--r--crates/tor-config/src/load.rs62
-rw-r--r--crates/tor-config/src/path.rs14
-rw-r--r--crates/tor-config/src/sources.rs72
-rw-r--r--crates/tor-ptmgr/Cargo.toml4
-rw-r--r--crates/tor-ptmgr/src/ipc.rs2
17 files changed, 159 insertions, 106 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 650abfa7b..206a1b080 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -204,7 +204,6 @@ dependencies = [
"backtrace",
"cfg-if",
"clap",
- "config",
"derive_builder_fork_arti",
"derive_more",
"educe",
@@ -388,7 +387,6 @@ dependencies = [
"async-trait",
"cfg-if",
"clap",
- "config",
"futures",
"pin-project",
"rand",
@@ -5803,6 +5801,7 @@ dependencies = [
"derive_builder_fork_arti",
"fs-mistrust",
"futures",
+ "itertools 0.12.1",
"serde",
"thiserror",
"tokio",
diff --git a/crates/arti-client/src/config.rs b/crates/arti-client/src/config.rs
index dc09ce49a..d82d6e7c3 100644
--- a/crates/arti-client/src/config.rs
+++ b/crates/arti-client/src/config.rs
@@ -924,8 +924,8 @@ mod test {
// here, so we'll just make sure it does _something_ plausible.
let dflt = default_config_files().unwrap();
- assert!(dflt[0].as_path().ends_with("arti.toml"));
- assert!(dflt[1].as_path().ends_with("arti.d"));
+ assert!(dflt[0].as_path().unwrap().ends_with("arti.toml"));
+ assert!(dflt[1].as_path().unwrap().ends_with("arti.d"));
assert_eq!(dflt.len(), 2);
}
diff --git a/crates/arti-testing/Cargo.toml b/crates/arti-testing/Cargo.toml
index 92eb68981..da9ef33c0 100644
--- a/crates/arti-testing/Cargo.toml
+++ b/crates/arti-testing/Cargo.toml
@@ -21,7 +21,8 @@ full = [
"tor-dirmgr/full",
"tor-error/full",
"tor-netdoc/full",
- "tor-rtcompat/full", "tor-basic-utils/full",
+ "tor-rtcompat/full",
+ "tor-basic-utils/full",
]
[dependencies]
@@ -31,7 +32,6 @@ arti-client = { package = "arti-client", path = "../arti-client", version = "0.1
async-trait = "0.1.54"
cfg-if = "1.0.0"
clap = { version = "4.3.24", features = ["wrap_help"] }
-config = { version = "0.14.0", default-features = false }
futures = "0.3.14"
pin-project = "1"
rand = "0.8"
diff --git a/crates/arti/Cargo.toml b/crates/arti/Cargo.toml
index 1f742de9c..80f1270d1 100644
--- a/crates/arti/Cargo.toml
+++ b/crates/arti/Cargo.toml
@@ -100,7 +100,6 @@ async-ctrlc = { version = "1.2.0", optional = true }
backtrace = "0.3.68"
cfg-if = "1.0.0"
clap = { version = "4.3.24", features = ["string", "wrap_help"] }
-config = { version = "0.14.0", default-features = false, features = ["toml"] }
derive_builder = { version = "0.11", package = "derive_builder_fork_arti" }
educe = "0.4.6"
fs-mistrust = { path = "../fs-mistrust", version = "0.7.7" }
diff --git a/crates/arti/src/cfg.rs b/crates/arti/src/cfg.rs
index 1d77072a9..f87a7251f 100644
--- a/crates/arti/src/cfg.rs
+++ b/crates/arti/src/cfg.rs
@@ -601,7 +601,9 @@ mod test {
fn default_config() {
use InExample::*;
- let empty_config = config::Config::builder().build().unwrap();
+ let empty_config = tor_config::ConfigurationSources::new_empty()
+ .load()
+ .unwrap();
let empty_config: ArtiCombinedConfig = tor_config::resolve(empty_config).unwrap();
let default = (ArtiConfig::default(), TorClientConfig::default());
@@ -662,10 +664,14 @@ mod test {
}
let parses_to_defaults = |example: &str, which: WhichExample, uncommented: bool| {
- let cfg = config::Config::builder()
- .add_source(config::File::from_str(example, config::FileFormat::Toml))
- .build()
- .unwrap();
+ let cfg = {
+ let mut sources = tor_config::ConfigurationSources::new_empty();
+ sources.push_source(
+ tor_config::ConfigurationSource::from_verbatim(example.to_string()),
+ tor_config::sources::MustRead::MustRead,
+ );
+ sources.load().unwrap()
+ };
// This tests that the example settings do not *contradict* the defaults.
let results: ResolutionResults<ArtiCombinedConfig> =
@@ -1242,11 +1248,15 @@ example config file {which:?}, uncommented={uncommented:?}
/// Make a TOML document of this section and parse it as a complete configuration.
/// Panic if the section cannot be parsed.
- fn parse(&self) -> config::Config {
+ fn parse(&self) -> tor_config::ConfigurationTree {
let s = self.build_string();
eprintln!("parsing\n --\n{}\n --", &s);
- let c: toml::Value = toml::from_str(&s).expect(&s);
- config::Config::try_from(&c).expect(&s)
+ let mut sources = tor_config::ConfigurationSources::new_empty();
+ sources.push_source(
+ tor_config::ConfigurationSource::from_verbatim(s.to_string()),
+ tor_config::sources::MustRead::MustRead,
+ );
+ sources.load().expect(&s)
}
fn resolve<R: tor_config::load::Resolvable>(&self) -> Result<R, ConfigResolveError> {
diff --git a/crates/arti/src/reload_cfg.rs b/crates/arti/src/reload_cfg.rs
index 0a664dc8c..41b0638e7 100644
--- a/crates/arti/src/reload_cfg.rs
+++ b/crates/arti/src/reload_cfg.rs
@@ -322,6 +322,7 @@ impl FileWatcherBuilder {
match source {
ConfigurationSource::Dir(dir) => self.watch_dir(dir)?,
ConfigurationSource::File(file) => self.watch_file(file)?,
+ ConfigurationSource::Verbatim(_) => {}
}
}
Ok(sources)
diff --git a/crates/tor-config/Cargo.toml b/crates/tor-config/Cargo.toml
index 680a5e7aa..14c2c2805 100644
--- a/crates/tor-config/Cargo.toml
+++ b/crates/tor-config/Cargo.toml
@@ -51,7 +51,6 @@ tracing = "0.1.36"
void = "1"
[dev-dependencies]
-config = { version = "0.14.0", default-features = false, features = ["toml"] }
dirs = "5.0.0"
rmp-serde = "1"
serde_json = "1.0.50"
diff --git a/crates/tor-config/README.md b/crates/tor-config/README.md
index 48a254243..cdea6d420 100644
--- a/crates/tor-config/README.md
+++ b/crates/tor-config/README.md
@@ -25,7 +25,7 @@ works as follows:
2. [`ConfigurationSources::load`] actually *reads* all of these sources,
parses them (eg, as TOML files),
- and returns a [`config::Config`].
+ and returns a [`ConfigurationTree`].
This is a tree-structured dynamically typed data structure,
mirroring the input configuration structure, largely unvalidated,
and containing everything in the input config sources.
diff --git a/crates/tor-config/semver.md b/crates/tor-config/semver.md
new file mode 100644
index 000000000..1c39155ce
--- /dev/null
+++ b/crates/tor-config/semver.md
@@ -0,0 +1,5 @@
+BREAKING: Several re-exported macro dependencies are now hidden.
+BREAKING: ConfigurationSource can now be Verbatim
+BREAKING: ConfigurationSource::as_path() has a new return type; AsRef<Path> is gone.
+BREAKING: config::Config is no longer exposed.
+
diff --git a/crates/tor-config/src/err.rs b/crates/tor-config/src/err.rs
index a3a23b4e8..993b240b2 100644
--- a/crates/tor-config/src/err.rs
+++ b/crates/tor-config/src/err.rs
@@ -130,12 +130,16 @@ impl HasKind for ReconfigureError {
}
}
-/// Wrapper for [`config::ConfigError`] with a more helpful error message.
+/// Wrapper for an error type from our underlying configuration library.
#[derive(Debug, Clone)]
pub struct ConfigError(Arc<config::ConfigError>);
-impl From<config::ConfigError> for ConfigError {
- fn from(err: config::ConfigError) -> Self {
+impl ConfigError {
+ /// Wrap `err` as a ConfigError.
+ ///
+ /// This is not a From implementation, since we don't want to expose our
+ /// underlying configuration library.
+ pub(crate) fn from_cfg_err(err: config::ConfigError) -> Self {
ConfigError(Arc::new(err))
}
}
@@ -157,13 +161,6 @@ impl std::error::Error for ConfigError {
}
}
-impl ConfigError {
- /// Return the inner [`config::ConfigError`] that this is wrapping.
- pub fn inner(&self) -> &config::ConfigError {
- self.0.as_ref()
- }
-}
-
#[cfg(test)]
mod test {
// @@ begin test lint list maintained by maint/add_warning @@
diff --git a/crates/tor-config/src/lib.rs b/crates/tor-config/src/lib.rs
index d6e340a0c..6c29fd541 100644
--- a/crates/tor-config/src/lib.rs
+++ b/crates/tor-config/src/lib.rs
@@ -51,28 +51,49 @@ mod mut_cfg;
mod path;
pub mod sources;
+#[doc(hidden)]
+pub mod deps {
+ pub use config;
+ pub use educe;
+ pub use itertools::Itertools;
+ pub use paste::paste;
+ pub use serde;
+ pub use tor_basic_utils::macro_first_nonempty;
+}
+
pub use cmdline::CmdLine;
-pub use config as config_crate;
-pub use educe;
pub use err::{ConfigBuildError, ConfigError, ReconfigureError};
pub use flatten::{Flatten, Flattenable};
-pub use itertools::Itertools;
pub use list_builder::{MultilineListBuilder, MultilineListBuilderError};
pub use load::{resolve, resolve_ignore_warnings, resolve_return_results};
pub use misc::*;
pub use mut_cfg::MutCfg;
-pub use paste::paste;
pub use path::{CfgPath, CfgPathError};
-pub use serde;
pub use sources::{ConfigurationSource, ConfigurationSources};
-pub use tor_basic_utils::macro_first_nonempty;
+use itertools::Itertools;
#[doc(hidden)]
pub use derive_adhoc;
#[doc(hidden)]
pub use flatten::flattenable_extract_fields;
+/// A set of configuration fields, represented as a set of nested K=V
+/// mappings.
+///
+/// (This is a wrapper for an underlying type provided by the library that
+/// actually does our configuration.)
+#[derive(Clone, Debug)]
+pub struct ConfigurationTree(config::Config);
+
+#[cfg(test)]
+impl ConfigurationTree {
+ #[cfg(test)]
+ pub(crate) fn get_string(&self, key: &str) -> Result<String, crate::ConfigError> {
+ self.0.get_string(key).map_err(ConfigError::from_cfg_err)
+ }
+}
+
/// Rules for reconfiguring a running Arti instance.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[non_exhaustive]
@@ -315,7 +336,7 @@ where
/// * `impl Default for $Config`
/// * `impl Builder for $ConfigBuilder`
/// * a self-test that the `Default` impl actually works
-/// * a test that the `Builder` can be deserialized from an empty [`config::Config`],
+/// * a test that the `Builder` can be deserialized from an empty [`ConfigurationTree`],
/// and then built, and that the result is the same as the ordinary default.
//
// The implementation munches fake "trait bounds" (`: !Deserialize + !Wombat ...`) off the RHS.
@@ -383,7 +404,7 @@ macro_rules! impl_standard_builder {
@ ( $($Builder :ident)? )
( $($default :ident)? )
( $($try_deserialize:ident)? ) $Config:ty : $(+)?
- } => { $crate::paste!{
+ } => { $crate::deps::paste!{
impl $Config {
/// Returns a fresh, default, builder
pub fn builder() -> [< $Config Builder >] {
@@ -420,7 +441,7 @@ macro_rules! impl_standard_builder {
if let Some(def) = def {
$( // expands iff there was $try_deserialize, which is always try_deserialize
- let empty_config = $crate::config_crate::Config::builder().build().unwrap();
+ let empty_config = $crate::deps::config::Config::builder().build().unwrap();
let builder: [< $Config Builder >] = empty_config.$try_deserialize().unwrap();
let from_empty = builder.build().unwrap();
assert_eq!(def, from_empty);
diff --git a/crates/tor-config/src/list_builder.rs b/crates/tor-config/src/list_builder.rs
index 1db8727b1..51570642c 100644
--- a/crates/tor-config/src/list_builder.rs
+++ b/crates/tor-config/src/list_builder.rs
@@ -225,8 +225,8 @@ macro_rules! define_list_builder_helper {
$( item_build: $item_build:expr; )?
$(#[ serde $serde_attrs:tt ] )+
} => {
- #[derive($crate::educe::Educe, Clone, Debug)]
- #[derive($crate::serde::Serialize, $crate::serde::Deserialize)]
+ #[derive($crate::deps::educe::Educe, Clone, Debug)]
+ #[derive($crate::deps::serde::Serialize, $crate::deps::serde::Deserialize)]
#[educe(Default)]
$(#[ serde $serde_attrs ])+
$(#[ $docs_and_attrs ])*
@@ -263,7 +263,7 @@ macro_rules! define_list_builder_helper {
let $things = $things
.iter()
.map(
- $crate::macro_first_nonempty!{
+ $crate::deps::macro_first_nonempty!{
[ $( $item_build )? ],
[ |item| item.build() ],
}
@@ -356,7 +356,7 @@ macro_rules! define_list_builder_accessors {
)*
}
} => {
- impl $OuterBuilder { $( $crate::paste!{
+ impl $OuterBuilder { $( $crate::deps::paste!{
/// Access the being-built list (resolving default)
///
/// If the field has not yet been set or accessed, the default list will be
diff --git a/crates/tor-config/src/load.rs b/crates/tor-config/src/load.rs
index 2ad34a925..506cb8384 100644
--- a/crates/tor-config/src/load.rs
+++ b/crates/tor-config/src/load.rs
@@ -1,8 +1,8 @@
-//! Processing a config::Config into a validated configuration
+//! Processing a `ConfigurationTree` into a validated configuration
//!
//! This module, and particularly [`resolve`], takes care of:
//!
-//! * Deserializing a [`config::Config`] into various `FooConfigBuilder`
+//! * Deserializing a [`ConfigurationTree`] into various `FooConfigBuilder`
//! * Calling the `build()` methods to get various `FooConfig`.
//! * Reporting unrecognised configuration keys
//! (eg to help the user detect misspellings).
@@ -21,7 +21,7 @@
//!
//! * [`impl TopLevel`](TopLevel) for your *top level* structures (only).
//!
-//! * Call [`resolve`] (or one of its variants) with a `config::Config`,
+//! * Call [`resolve`] (or one of its variants) with a `ConfigurationTree`,
//! to obtain your top-level configuration(s).
//!
//! # Example
@@ -33,7 +33,7 @@
//! for additional configuration settings for the added features.
//! * Establishes some configuration sources
//! (the trivial empty `ConfigSources`, to avoid clutter in the example)
-//! * Reads those sources into a single configuration taxonomy [`config::Config`].
+//! * Reads those sources into a single configuration taxonomy [`ConfigurationTree`].
//! * Processes that configuration into a 3-tuple of configuration
//! structs for the three components, namely:
//! - `TorClientConfig`, the configuration for the `arti_client` crate's `TorClient`
@@ -96,7 +96,7 @@ use serde::de::DeserializeOwned;
use thiserror::Error;
use tracing::warn;
-use crate::ConfigBuildError;
+use crate::{ConfigBuildError, ConfigurationTree};
/// Error resolving a configuration (during deserialize, or build)
#[derive(Error, Debug)]
@@ -111,12 +111,6 @@ pub enum ConfigResolveError {
Build(#[from] ConfigBuildError),
}
-impl From<config::ConfigError> for ConfigResolveError {
- fn from(err: config::ConfigError) -> Self {
- crate::ConfigError::from(err).into()
- }
-}
-
/// A type that can be built from a builder via a build method
pub trait Builder {
/// The type that this builder constructs
@@ -230,7 +224,7 @@ define_for_tuples! { A - B C D E }
/// You don't want to try to obtain one.
pub struct ResolveContext {
/// The input
- input: config::Config,
+ input: ConfigurationTree,
/// Paths unrecognized by all deserializations
///
@@ -303,7 +297,7 @@ enum PathEntry {
///
/// Inner function used by all the `resolve_*` family
fn resolve_inner<T>(
- input: config::Config,
+ input: ConfigurationTree,
want_disfavoured: bool,
) -> Result<ResolutionResults<T>, ConfigResolveError>
where
@@ -314,7 +308,7 @@ where
if want_disfavoured {
T::enumerate_deprecated_keys(&mut |l: &[&str]| {
for key in l {
- match input.get(key) {
+ match input.0.get(key) {
Err(_) => {}
Ok(serde::de::IgnoredAny) => {
deprecated.insert(key);
@@ -373,7 +367,7 @@ where
///
/// For an example, see the
/// [`tor_config::load` module-level documentation](self).
-pub fn resolve<T>(input: config::Config) -> Result<T, ConfigResolveError>
+pub fn resolve<T>(input: ConfigurationTree) -> Result<T, ConfigResolveError>
where
T: Resolvable,
{
@@ -393,7 +387,7 @@ where
/// Deserialize and build overall configuration, reporting unrecognized keys in the return value
pub fn resolve_return_results<T>(
- input: config::Config,
+ input: ConfigurationTree,
) -> Result<ResolutionResults<T>, ConfigResolveError>
where
T: Resolvable,
@@ -416,7 +410,7 @@ pub struct ResolutionResults<T> {
}
/// Deserialize and build overall configuration, silently ignoring unrecognized config keys
-pub fn resolve_ignore_warnings<T>(input: config::Config) -> Result<T, ConfigResolveError>
+pub fn resolve_ignore_warnings<T>(input: ConfigurationTree) -> Result<T, ConfigResolveError>
where
T: Resolvable,
{
@@ -436,13 +430,13 @@ where
// That is how this tracking is disabled when we want it to be.
let want_unrecognized = !input.unrecognized.is_empty();
let ret = if !want_unrecognized {
- deser.try_deserialize()
+ deser.0.try_deserialize()
} else {
let mut nign = BTreeSet::new();
let mut recorder = |path: serde_ignored::Path<'_>| {
nign.insert(copy_path(&path));
};
- let deser = serde_ignored::Deserializer::new(deser, &mut recorder);
+ let deser = serde_ignored::Deserializer::new(deser.0, &mut recorder);
let ret = serde::Deserialize::deserialize(deser);
if ret.is_err() {
// If we got an error, the config might only have been partially processed,
@@ -452,7 +446,7 @@ where
input.unrecognized.intersect_with(nign);
ret
};
- ret?
+ ret.map_err(crate::ConfigError::from_cfg_err)?
};
let built = builder.build()?;
Ok(built)
@@ -784,12 +778,14 @@ mod test {
a = "hi"
old = true
"#;
- let source = config::File::from_str(test_data, config::FileFormat::Toml);
-
- let cfg = config::Config::builder()
- .add_source(source)
- .build()
- .unwrap();
+ let cfg = {
+ let mut sources = crate::ConfigurationSources::new_empty();
+ sources.push_source(
+ crate::ConfigurationSource::from_verbatim(test_data.to_string()),
+ crate::sources::MustRead::MustRead,
+ );
+ sources.load().unwrap()
+ };
let _: (TestConfigA, TestConfigB) = resolve_ignore_warnings(cfg.clone()).unwrap();
@@ -836,11 +832,15 @@ mod test {
// suppress a dead-code warning.
let _b = TestConfigC::builder();
- let source = config::File::from_str(test_data, config::FileFormat::Toml);
- let cfg = config::Config::builder()
- .add_source(source)
- .build()
- .unwrap();
+ let cfg = {
+ let mut sources = crate::ConfigurationSources::new_empty();
+ sources.push_source(
+ crate::ConfigurationSource::from_verbatim(test_data.to_string()),
+ crate::sources::MustRead::MustRead,
+ );
+ sources.load().unwrap()
+ };
+
{
// First try "A", then "C".
let res1: Result<ResolutionResults<(TestConfigA, TestConfigC)>, _> =
diff --git a/crates/tor-config/src/path.rs b/crates/tor-config/src/path.rs
index aa7cc6f2f..476d04ae2 100644
--- a/crates/tor-config/src/path.rs
+++ b/crates/tor-config/src/path.rs
@@ -405,13 +405,15 @@ mod test_serde {
}
fn deser_toml_cfg(toml: &str) -> CfgPath {
dbg!(toml);
- let cfg = config::File::from_str(toml, config::FileFormat::Toml);
- let cfg = config::Config::builder()
- .add_source(cfg)
- .build()
- .expect("parse toml failed");
+ let mut sources = crate::ConfigurationSources::new_empty();
+ sources.push_source(
+ crate::ConfigurationSource::from_verbatim(toml.to_string()),
+ crate::sources::MustRead::MustRead,
+ );
+ let cfg = sources.load().unwrap();
+
dbg!(&cfg);
- let TestConfigFile { p } = cfg.try_deserialize().expect("deser cfg failed");
+ let TestConfigFile { p } = cfg.0.try_deserialize().expect("deser cfg failed");
p
}
diff --git a/crates/tor-config/src/sources.rs b/crates/tor-config/src/sources.rs
index 00a64a623..687952bfa 100644
--- a/crates/tor-config/src/sources.rs
+++ b/crates/tor-config/src/sources.rs
@@ -2,7 +2,8 @@
//!
//! This module provides [`ConfigurationSources`].
//!
-//! This layer brings together the functionality of [`config::File`],
+//! This layer brings together the functionality of
+//! our underlying configuration library,
//! [`fs_mistrust`] and [`tor_config::cmdline`](crate::cmdline).
//!
//! A `ConfigurationSources` records a set of filenames of TOML files,
@@ -12,7 +13,7 @@
//! Usually, call [`ConfigurationSources::from_cmdline`],
//! perhaps [`set_mistrust`](ConfigurationSources::set_mistrust),
//! and finally [`load`](ConfigurationSources::load).
-//! The resulting [`config::Config`] can then be deserialized.
+//! The resulting [`ConfigurationTree`] can then be deserialized.
//!
//! If you want to watch for config file changes,
//! use [`ConfigurationSources::scan()`],
@@ -23,12 +24,12 @@
//! which is necessary to avoid possibly missing changes.)
use std::ffi::OsString;
-use std::{fs, io};
+use std::{fs, io, sync::Arc};
use void::ResultVoidExt as _;
use crate::err::ConfigError;
-use crate::CmdLine;
+use crate::{CmdLine, ConfigurationTree};
/// The synchronous configuration builder type we use.
///
@@ -68,13 +69,16 @@ pub enum MustRead {
/// You can make one out of a `PathBuf`, examining its syntax like `arti` does,
/// using `ConfigurationSource::from_path`.
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
-#[allow(clippy::exhaustive_enums)] // Callers will need to understand this
+#[allow(clippy::exhaustive_enums)]
pub enum ConfigurationSource {
/// A plain file
File(PathBuf),
/// A directory
Dir(PathBuf),
+
+ /// A verbatim TOML file
+ Verbatim(Arc<String>),
}
impl ConfigurationSource {
@@ -94,17 +98,17 @@ impl ConfigurationSource {
}
}
- /// Return a reference to the inner `Path`
- pub fn as_path(&self) -> &Path {
- self.as_ref()
+ /// Use the provided text as verbatim TOML, as if it had been read from disk.
+ pub fn from_verbatim(text: String) -> ConfigurationSource {
+ Self::Verbatim(Arc::new(text))
}
-}
-impl AsRef<PathBuf> for ConfigurationSource {
- fn as_ref(&self) -> &PathBuf {
+ /// Return a reference to the inner `Path`, if there is one.
+ pub fn as_path(&self) -> Option<&Path> {
use ConfigurationSource as CS;
match self {
- CS::File(p) | CS::Dir(p) => p,
+ CS::File(p) | CS::Dir(p) => Some(p),
+ CS::Verbatim(_) => None,
}
}
}
@@ -248,11 +252,11 @@ impl ConfigurationSources {
&self.mistrust
}
- /// Scan for files and load the configuration into a new [`config::Config`].
+ /// Scan for files and load the configuration into a new [`ConfigurationTree`].
///
/// This is a convenience method for [`scan()`](Self::scan)
/// followed by [`files.load`].
- pub fn load(&self) -> Result<config::Config, ConfigError> {
+ pub fn load(&self) -> Result<ConfigurationTree, ConfigError> {
let files = self.scan()?;
files.load()
}
@@ -270,12 +274,13 @@ impl ConfigurationSources {
if e.kind() == io::ErrorKind::NotFound && !required {
Result::<_, crate::ConfigError>::Ok(())
} else {
- Err(config::ConfigError::Message(format!(
- "unable to access config path: {:?}: {}",
- &source.as_path(),
- e
+ Err(crate::ConfigError::from_cfg_err(
+ config::ConfigError::Message(format!(
+ "unable to access config path: {:?}: {}",
+ &source.as_path(),
+ e
+ )),
))
- .into())
}
};
@@ -319,7 +324,7 @@ impl ConfigurationSources {
must_read: MustRead::TolerateAbsence,
}));
}
- CS::File(_) => {
+ CS::File(_) | CS::Verbatim(_) => {
out.push(FoundConfigFile {
source: source.clone(),
must_read,
@@ -354,6 +359,11 @@ impl FoundConfigFiles<'_> {
let file = match source {
CS::File(file) => file,
CS::Dir(_) => continue,
+ CS::Verbatim(text) => {
+ builder =
+ builder.add_source(config::File::from_str(&text, config::FileFormat::Toml));
+ continue;
+ }
};
match self
@@ -383,11 +393,13 @@ impl FoundConfigFiles<'_> {
Ok(builder)
}
- /// Load the configuration into a new [`config::Config`].
- pub fn load(self) -> Result<config::Config, ConfigError> {
+ /// Load the configuration into a new [`ConfigurationTree`].
+ pub fn load(self) -> Result<ConfigurationTree, ConfigError> {
let mut builder = config::Config::builder();
builder = self.add_sources(builder)?;
- Ok(builder.build()?)
+ Ok(ConfigurationTree(
+ builder.build().map_err(ConfigError::from_cfg_err)?,
+ ))
}
}
@@ -422,7 +434,7 @@ fn foreign_err<E>(err: E) -> crate::ConfigError
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
- crate::ConfigError::from(config::ConfigError::Foreign(err.into()))
+ crate::ConfigError::from_cfg_err(config::ConfigError::Foreign(err.into()))
}
#[cfg(test)]
@@ -474,7 +486,7 @@ friends = 4242
fn load_nodefaults<P: AsRef<Path>>(
files: &[(P, MustRead)],
opts: &[String],
- ) -> Result<config::Config, crate::ConfigError> {
+ ) -> Result<ConfigurationTree, crate::ConfigError> {
sources_nodefaults(files, opts).load()
}
@@ -527,7 +539,13 @@ world = \"nonsense\"
assert_eq!(
found
.iter()
- .map(|p| p.as_path().strip_prefix(&td).unwrap().to_str().unwrap())
+ .map(|p| p
+ .as_path()
+ .unwrap()
+ .strip_prefix(&td)
+ .unwrap()
+ .to_str()
+ .unwrap())
.collect_vec(),
&["1.toml", "extra.d", "extra.d/2.toml"]
);
@@ -571,7 +589,7 @@ world = \"nonsense\"
let files: Vec<_> = sources
.files
.iter()
- .map(|file| file.0.as_ref().to_str().unwrap())
+ .map(|file| file.0.as_path().unwrap().to_str().unwrap())
.collect();
assert_eq!(files, vec!["/family/yor.toml", "/family/anya.toml"]);
assert_eq!(sources.files[0].1, MustRead::MustRead);
diff --git a/crates/tor-ptmgr/Cargo.toml b/crates/tor-ptmgr/Cargo.toml
index a51f50337..9d6361ca5 100644
--- a/crates/tor-ptmgr/Cargo.toml
+++ b/crates/tor-ptmgr/Cargo.toml
@@ -22,7 +22,8 @@ full = [
"tor-error/full",
"tor-linkspec/full",
"tor-rtcompat/full",
- "tor-socksproto/full", "tor-async-utils/full",
+ "tor-socksproto/full",
+ "tor-async-utils/full",
]
experimental = ["experimental-api"]
@@ -34,6 +35,7 @@ async-trait = "0.1.54"
derive_builder = { version = "0.11.2", package = "derive_builder_fork_arti" }
fs-mistrust = { version = "0.7.7", path = "../fs-mistrust" }
futures = "0.3.14"
+itertools = "0.12.0"
serde = { version = "1.0.103", features = ["derive"] }
thiserror = "1"
tor-async-utils = { version = "0.1.5", path = "../tor-async-utils" }
diff --git a/crates/tor-ptmgr/src/ipc.rs b/crates/tor-ptmgr/src/ipc.rs
index cd65fb19c..50e15b1b0 100644
--- a/crates/tor-ptmgr/src/ipc.rs
+++ b/crates/tor-ptmgr/src/ipc.rs
@@ -9,6 +9,7 @@ use crate::err::PtError;
use futures::channel::mpsc;
use futures::channel::mpsc::Receiver;
use futures::StreamExt;
+use itertools::Itertools;
use std::borrow::Cow;
use std::collections::HashMap;
use std::ffi::OsString;
@@ -21,7 +22,6 @@ use std::sync::Arc;
use std::time::{Duration, Instant};
use std::{io, thread};
use tor_basic_utils::PathExt as _;
-use tor_config::Itertools;
use tor_error::{internal, warn_report};
use tor_linkspec::PtTransportName;
use tor_rtcompat::{Runtime, SleepProviderExt};