use std::path::Path;
use std::{collections::HashMap, path::PathBuf};
use derive_builder::Builder;
use derive_more::AsRef;
use fs_mistrust::{Mistrust, MistrustBuilder};
use serde::{Deserialize, Serialize};
use tor_chanmgr::{ChannelConfig, ChannelConfigBuilder};
use tor_config::{impl_standard_builder, mistrust::BuilderExt, ConfigBuildError};
use tor_config_path::CfgPath;
use tor_keymgr::config::{ArtiKeystoreConfig, ArtiKeystoreConfigBuilder};
#[derive(Clone, Builder, Debug, Eq, PartialEq, AsRef)]
#[builder(build_fn(error = "ConfigBuildError"))]
#[builder(derive(Serialize, Deserialize, Debug))]
#[non_exhaustive]
pub(crate) struct TorRelayConfig {
#[builder(sub_builder)]
#[builder_field_attr(serde(default))]
pub(crate) storage: StorageConfig,
#[builder(
sub_builder,
field(
type = "HashMap<String, i32>",
build = "default_extend(self.override_net_params.clone())"
)
)]
#[builder_field_attr(serde(default))]
pub(crate) override_net_params: tor_netdoc::doc::netstatus::NetParams<i32>,
#[builder(sub_builder)]
#[builder_field_attr(serde(default))]
pub(crate) channel: ChannelConfig,
}
impl_standard_builder! { TorRelayConfig }
#[allow(unused)] impl TorRelayConfigBuilder {
pub(crate) fn from_directories<P, Q>(state_dir: P, cache_dir: Q) -> Self
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let mut builder = Self::default();
builder
.storage()
.cache_dir(CfgPath::new_literal(cache_dir.as_ref()))
.state_dir(CfgPath::new_literal(state_dir.as_ref()));
builder
}
}
fn default_extend<T: Default + Extend<X>, X>(to_add: impl IntoIterator<Item = X>) -> T {
let mut collection = T::default();
collection.extend(to_add);
collection
}
#[derive(Debug, Clone, Builder, Eq, PartialEq)]
#[builder(build_fn(error = "ConfigBuildError"))]
#[builder(derive(Debug, Serialize, Deserialize))]
#[non_exhaustive]
pub(crate) struct StorageConfig {
#[builder(setter(into), default = "default_cache_dir()")]
cache_dir: CfgPath,
#[builder(setter(into), default = "default_state_dir()")]
state_dir: CfgPath,
#[builder(sub_builder)]
#[builder_field_attr(serde(default))]
keystore: ArtiKeystoreConfig,
#[builder(sub_builder(fn_name = "build_for_arti"))]
#[builder_field_attr(serde(default))]
permissions: Mistrust,
}
impl_standard_builder! { StorageConfig }
impl StorageConfig {
pub(crate) fn permissions(&self) -> &Mistrust {
&self.permissions
}
pub(crate) fn keystore_dir(&self) -> Result<PathBuf, ConfigBuildError> {
let r = tor_config_path::CfgPathResolver::default();
Ok(self
.state_dir
.path(&r)
.map_err(|e| ConfigBuildError::Invalid {
field: "state_dir".to_owned(),
problem: e.to_string(),
})?
.join("keystore"))
}
}
fn default_cache_dir() -> CfgPath {
CfgPath::new("${ARTI_RELAY_CACHE}".to_owned())
}
fn default_state_dir() -> CfgPath {
CfgPath::new("${ARTI_RELAY_LOCAL_DATA}".to_owned())
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn defaults() {
let dflt = TorRelayConfig::default();
let b2 = TorRelayConfigBuilder::default();
let dflt2 = b2.build().unwrap();
assert_eq!(&dflt, &dflt2);
}
#[test]
fn builder() {
let mut bld = TorRelayConfigBuilder::default();
bld.storage()
.cache_dir(CfgPath::new("/var/tmp/foo".to_owned()))
.state_dir(CfgPath::new("/var/tmp/bar".to_owned()));
let val = bld.build().unwrap();
assert_ne!(val, TorRelayConfig::default());
}
}