tether_agent/agent/
builder.rs

1use std::sync::{mpsc, Arc, Mutex};
2
3use log::*;
4
5use crate::tether_compliant_topic::TetherOrCustomTopic;
6
7use super::TetherAgent;
8
9const DEFAULT_USERNAME: &str = "tether";
10const DEFAULT_PASSWORD: &str = "sp_ceB0ss!";
11
12/**
13Typically, you will use this to construct a well-configured TetherAgent with a combination
14of sensible defaults and custom overrides.
15
16Make a new instance of TetherAgentBuilder with `TetherAgentBuilder::new()`, chain whatever
17overrides you might need, and finally call `build()` to get the actual TetherAgent instance.
18*/
19#[derive(Clone)]
20pub struct TetherAgentBuilder {
21    role: String,
22    id: Option<String>,
23    protocol: Option<String>,
24    host: Option<String>,
25    port: Option<u16>,
26    username: Option<String>,
27    password: Option<String>,
28    base_path: Option<String>,
29    auto_connect: bool,
30    mqtt_client_id: Option<String>,
31}
32
33impl TetherAgentBuilder {
34    /// Initialise Tether Options struct with default options; call other methods to customise.
35    /// Call `build()` to get the actual TetherAgent instance (and connect automatically, by default)
36    pub fn new(role: &str) -> Self {
37        TetherAgentBuilder {
38            role: String::from(role),
39            id: None,
40            protocol: None,
41            host: None,
42            port: None,
43            username: None,
44            password: None,
45            base_path: None,
46            auto_connect: true,
47            mqtt_client_id: None,
48        }
49    }
50
51    /// Optionally sets the **Tether ID**, as used in auto-generating topics such as `myRole/myID/myChannel` _not_ the MQTT Client ID.
52    /// Provide Some(value) to override or None to use the default `any` (when publishing) or `+` when subscribing.
53    pub fn id(mut self, id: Option<&str>) -> Self {
54        self.id = id.map(|x| x.into());
55        self
56    }
57
58    /// Provide Some(value) to override or None to use default
59    pub fn protocol(mut self, protocol: Option<&str>) -> Self {
60        self.protocol = protocol.map(|x| x.into());
61        self
62    }
63
64    /// Optionally set the **MQTT Client ID** used when connecting to the MQTT broker. This is _not_ the same as the **Tether ID**
65    /// used for auto-generating topics.
66    ///
67    /// By default we use a UUID for this value, in order to avoid hard-to-debug issues where Tether Agent instances share
68    /// the same Client ID and therefore events/messages are not handled properly by all instances.
69    pub fn mqtt_client_id(mut self, client_id: Option<&str>) -> Self {
70        self.mqtt_client_id = client_id.map(|x| x.into());
71        self
72    }
73
74    /// Provide Some(value) to override or None to use default
75    pub fn host(mut self, host: Option<&str>) -> Self {
76        self.host = host.map(|x| x.into());
77        self
78    }
79
80    pub fn port(mut self, port: Option<u16>) -> Self {
81        self.port = port;
82        self
83    }
84
85    /// Provide Some(value) to override or None to use default
86    pub fn username(mut self, username: Option<&str>) -> Self {
87        self.username = username.map(|x| x.into());
88        self
89    }
90
91    /// Provide Some(value) to override or None to use default
92    pub fn password(mut self, password: Option<&str>) -> Self {
93        self.password = password.map(|x| x.into());
94        self
95    }
96
97    /// Provide Some(value) to override or None to use default
98    pub fn base_path(mut self, base_path: Option<&str>) -> Self {
99        self.base_path = base_path.map(|x| x.into());
100        self
101    }
102
103    /// Specify explicitly whether to attempt auto-connection on build;
104    /// if set to `false` you will need to connect the TetherAgent (and therefore
105    /// its underlying MQTT client) yourself after creating the instance.
106    pub fn auto_connect(mut self, should_auto_connect: bool) -> Self {
107        self.auto_connect = should_auto_connect;
108        self
109    }
110
111    /// Using a combination of sensible defaults and any overrides you might
112    /// have provided using other functions called on TetherAgentOptions, this
113    /// function returns a well-configured TetherAgent instance.
114    ///
115    /// Unless you set `.auto_connect(false)`, the TetherAgent will attempt to
116    /// connect to the MQTT broker automatically upon creation.
117    pub fn build(self) -> anyhow::Result<TetherAgent> {
118        let protocol = self.protocol.clone().unwrap_or("mqtt".into());
119        let host = self.host.clone().unwrap_or("localhost".into());
120        let port = self.port.unwrap_or(1883);
121        let username = self.username.unwrap_or(DEFAULT_USERNAME.into());
122        let password = self.password.unwrap_or(DEFAULT_PASSWORD.into());
123        let base_path = self.base_path.unwrap_or("/".into());
124
125        debug!(
126            "final build uses options protocol = {}, host = {}, port = {}",
127            protocol, host, port
128        );
129
130        let (message_sender, message_receiver) = mpsc::channel::<(TetherOrCustomTopic, Vec<u8>)>();
131
132        let mut agent = TetherAgent {
133            role: self.role.clone(),
134            id: self.id,
135            host,
136            port,
137            username,
138            password,
139            protocol,
140            base_path,
141            client: None,
142            message_sender,
143            message_receiver,
144            mqtt_client_id: self.mqtt_client_id,
145            is_connected: Arc::new(Mutex::new(false)),
146            auto_connect_enabled: self.auto_connect,
147        };
148
149        if self.auto_connect {
150            match agent.connect() {
151                Ok(()) => Ok(agent),
152                Err(e) => Err(e),
153            }
154        } else {
155            warn!("Auto-connect disabled; you must call .connect explicitly");
156            Ok(agent)
157        }
158    }
159}