1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
use crate::pubnub::PubNub;
use crate::runtime::Runtime;
use crate::subscription::subscribe_loop::ExitTx as SubscribeLoopExitTx;
use crate::subscription::subscribe_loop_supervisor::{
    SubscribeLoopSupervisor, SubscribeLoopSupervisorParams,
};
use crate::transport::Transport;
use futures_util::lock::Mutex;
use std::sync::Arc;

/// # PubNub Client Builder
///
/// Create a [`crate::PubNub`] client using the builder pattern.
/// Optional items can be overridden using this.
#[derive(Clone, Debug)]
pub struct Builder<TTransport = (), TRuntime = ()> {
    /// Transport to use for communication.
    transport: TTransport,
    /// Runtime to use for managing resources.
    runtime: TRuntime,

    /// Subscription related configuration params.
    /// If set, gets a signal when subscribe loop exits.
    subscribe_loop_exit_tx: Option<SubscribeLoopExitTx>,
}

impl<TTransport, TRuntime> Builder<TTransport, TRuntime>
where
    TTransport: Transport,
    TRuntime: Runtime,
{
    /// Build the [`PubNub`] client to begin streaming messages.
    ///
    /// # Example
    ///
    /// ```
    /// use pubnub_core::mock::{runtime::MockRuntime, transport::MockTransport};
    /// use pubnub_core::Builder;
    ///
    /// let transport = MockTransport::new();
    /// let runtime = MockRuntime::new();
    ///
    /// let pubnub = Builder::with_components(transport, runtime).build();
    /// ```
    #[must_use]
    pub fn build(self) -> PubNub<TTransport, TRuntime> {
        let Self {
            transport,
            runtime,
            subscribe_loop_exit_tx,
        } = self;

        let subscribe_loop_supervisor_params = SubscribeLoopSupervisorParams {
            exit_tx: subscribe_loop_exit_tx,
        };

        PubNub {
            transport,
            runtime,

            subscribe_loop_supervisor: Arc::new(Mutex::new(SubscribeLoopSupervisor::new(
                subscribe_loop_supervisor_params,
            ))),
        }
    }
}

impl<TTransport, TRuntime> Builder<TTransport, TRuntime> {
    /// Create a new [`Builder`] that can configure a [`PubNub`] client
    /// with custom component implementations.
    ///
    /// # Example
    ///
    /// ```
    /// use pubnub_core::mock::{runtime::MockRuntime, transport::MockTransport};
    /// use pubnub_core::Builder;
    ///
    /// let transport = MockTransport::new();
    /// let runtime = MockRuntime::new();
    ///
    /// let pubnub = Builder::with_components(transport, runtime).build();
    /// ```
    #[must_use]
    pub fn with_components(transport: TTransport, runtime: TRuntime) -> Self {
        Self {
            subscribe_loop_exit_tx: None,

            transport,
            runtime,
        }
    }

    /// Set the subscribe loop exit tx.
    ///
    /// If set, subscribe loop sends a message to it when it exits.
    ///
    /// # Example
    ///
    /// ```
    /// # use pubnub_core::mock::{transport::MockTransport, runtime::MockRuntime};
    /// # let transport = MockTransport::new();
    /// # let runtime = MockRuntime::new();
    /// use pubnub_core::Builder;
    ///
    /// let (tx, _rx) = futures_channel::mpsc::channel(1);
    ///
    /// let pubnub = Builder::with_components(transport, runtime)
    ///     .subscribe_loop_exit_tx(tx)
    ///     .build();
    /// ```
    #[must_use]
    pub fn subscribe_loop_exit_tx(mut self, tx: SubscribeLoopExitTx) -> Self {
        self.subscribe_loop_exit_tx = Some(tx);
        self
    }

    /// Set the transport to use.
    ///
    /// This allows changing the [`Transport`] type on the builder and,
    /// therefore, on the resulting [`PubNub`] client.
    #[must_use]
    pub fn transport<U: Transport>(self, transport: U) -> Builder<U, TRuntime> {
        Builder {
            transport,

            // Copy the rest of the fields.
            runtime: self.runtime,
            subscribe_loop_exit_tx: self.subscribe_loop_exit_tx,
        }
    }

    /// Set the runtime to use.
    ///
    /// This allows changing the [`Runtime`] type on the builder and,
    /// therefore, on the resulting [`PubNub`] client.
    #[must_use]
    pub fn runtime<U: Runtime>(self, runtime: U) -> Builder<TTransport, U> {
        Builder {
            runtime,

            // Copy the rest of the fields.
            transport: self.transport,
            subscribe_loop_exit_tx: self.subscribe_loop_exit_tx,
        }
    }
}

impl Builder<(), ()> {
    /// Create a new [`Builder`] that can configure a [`PubNub`] client.
    ///
    /// # Example
    ///
    /// ```
    /// use pubnub_core::mock::{runtime::MockRuntime, transport::MockTransport};
    /// use pubnub_core::Builder;
    ///
    /// let transport = MockTransport::new();
    /// let runtime = MockRuntime::new();
    ///
    /// let pubnub = Builder::new().transport(transport).runtime(runtime).build();
    /// ```
    #[must_use]
    pub fn new() -> Self {
        Self::with_components((), ())
    }
}

impl<TTransport, TRuntime> Default for Builder<TTransport, TRuntime>
where
    TTransport: Default,
    TRuntime: Default,
{
    /// Create a new [`Builder`] that can configure a [`PubNub`] client
    /// with default components.
    #[must_use]
    fn default() -> Self {
        Self::with_components(TTransport::default(), TRuntime::default())
    }
}