1
//! Types for conveniently constructing TorClients.
2

            
3
#![allow(missing_docs, clippy::missing_docs_in_private_items)]
4

            
5
use crate::{err::ErrorDetail, BootstrapBehavior, Result, TorClient, TorClientConfig};
6
use std::sync::Arc;
7
use tor_dirmgr::DirMgrConfig;
8
use tor_rtcompat::Runtime;
9

            
10
/// An object that knows how to construct some kind of DirProvider.
11
///
12
/// Note that this type is only actually exposed when the `experimental-api`
13
/// feature is enabled.
14
#[allow(unreachable_pub)]
15
pub trait DirProviderBuilder<R: Runtime> {
16
    fn build(
17
        &self,
18
        runtime: R,
19
        circmgr: Arc<tor_circmgr::CircMgr<R>>,
20
        config: DirMgrConfig,
21
    ) -> Result<Arc<dyn tor_dirmgr::DirProvider + Send + Sync + 'static>>;
22
}
23

            
24
/// A DirProviderBuilder that constructs a regular DirMgr.
25
#[derive(Clone, Debug)]
26
struct DirMgrBuilder {}
27

            
28
impl<R: Runtime> DirProviderBuilder<R> for DirMgrBuilder {
29
2
    fn build(
30
2
        &self,
31
2
        runtime: R,
32
2
        circmgr: Arc<tor_circmgr::CircMgr<R>>,
33
2
        config: DirMgrConfig,
34
2
    ) -> Result<Arc<dyn tor_dirmgr::DirProvider + Send + Sync + 'static>> {
35
2
        let dirmgr = tor_dirmgr::DirMgr::create_unbootstrapped(config, runtime, circmgr)
36
2
            .map_err(ErrorDetail::from)?;
37
2
        Ok(Arc::new(dirmgr))
38
2
    }
39
}
40

            
41
/// An object for constructing a [`TorClient`].
42
///
43
/// Returned by [`TorClient::builder()`].
44
#[derive(Clone)]
45
#[must_use]
46
pub struct TorClientBuilder<R: Runtime> {
47
    /// The runtime for the client to use
48
    runtime: R,
49
    /// The client's configuration.
50
    config: TorClientConfig,
51
    /// How the client should behave when it is asked to do something on the Tor
52
    /// network before `bootstrap()` is called.
53
    bootstrap_behavior: BootstrapBehavior,
54
    /// Optional object to construct a DirProvider.
55
    ///
56
    /// Wrapped in an Arc so that we don't need to force DirProviderBuilder to
57
    /// implement Clone.
58
    dirmgr_builder: Arc<dyn DirProviderBuilder<R>>,
59
}
60

            
61
impl<R: Runtime> TorClientBuilder<R> {
62
    /// Construct a new TorClientBuilder with the given runtime.
63
2
    pub(crate) fn new(runtime: R) -> Self {
64
2
        Self {
65
2
            runtime,
66
2
            config: TorClientConfig::default(),
67
2
            bootstrap_behavior: BootstrapBehavior::default(),
68
2
            dirmgr_builder: Arc::new(DirMgrBuilder {}),
69
2
        }
70
2
    }
71

            
72
    /// Set the configuration for the `TorClient` under construction.
73
    ///
74
    /// If not called, then a compiled-in default configuration will be used.
75
2
    pub fn config(mut self, config: TorClientConfig) -> Self {
76
2
        self.config = config;
77
2
        self
78
2
    }
79

            
80
    /// Set the bootstrap behavior for the `TorClient` under construction.
81
    ///
82
    /// If not called, then the default ([`BootstrapBehavior::OnDemand`]) will
83
    /// be used.
84
2
    pub fn bootstrap_behavior(mut self, bootstrap_behavior: BootstrapBehavior) -> Self {
85
2
        self.bootstrap_behavior = bootstrap_behavior;
86
2
        self
87
2
    }
88

            
89
    /// Override the default function used to construct the directory provider.
90
    ///
91
    /// Only available when compiled with the `experimental-api` feature: this
92
    /// code is unstable.
93
    #[cfg(all(feature = "experimental-api", feature = "error_detail"))]
94
    pub fn dirmgr_builder<B>(mut self, builder: Arc<dyn DirProviderBuilder<R>>) -> Self
95
    where
96
        B: DirProviderBuilder<R> + 'static,
97
    {
98
        self.dirmgr_builder = builder;
99
        self
100
    }
101

            
102
    /// Create a `TorClient` from this builder, without automatically launching
103
    /// the bootstrap process.
104
    ///
105
    /// If you have left the default [`BootstrapBehavior`] in place, the client
106
    /// will bootstrap itself as soon any attempt is made to use it.  You can
107
    /// also bootstrap the client yourself by running its
108
    /// [`bootstrap()`](TorClient::bootstrap) method.
109
    ///
110
    /// If you have replaced the default behavior with [`BootstrapBehavior::Manual`],
111
    /// any attempts to use the client will fail with an error of kind
112
    /// [`ErrorKind::BootstrapRequired`](crate::ErrorKind::BootstrapRequired),
113
    /// until you have called [`TorClient::bootstrap`] yourself.  
114
    /// This option is useful if you wish to have control over the bootstrap
115
    /// process (for example, you might wish to avoid initiating network
116
    /// connections until explicit user confirmation is given).
117
2
    pub fn create_unbootstrapped(self) -> Result<TorClient<R>> {
118
2
        TorClient::create_inner(
119
2
            self.runtime,
120
2
            self.config,
121
2
            self.bootstrap_behavior,
122
2
            self.dirmgr_builder.as_ref(),
123
2
        )
124
2
        .map_err(ErrorDetail::into)
125
2
    }
126

            
127
    /// Create a TorClient from this builder, and try to bootstrap it.
128
    pub async fn create_bootstrapped(self) -> Result<TorClient<R>> {
129
        let r = self.create_unbootstrapped()?;
130
        r.bootstrap().await?;
131
        Ok(r)
132
    }
133
}