tether_agent/agent/
builder.rs

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