clickatell_api/one_api/
blocking_client.rs

1use crate::one_api::{balance, message_status, send_messages};
2use crate::one_api::{error::Error, result::Result};
3use reqwest::{
4  blocking::Client as HTTPClient, header::HeaderMap, header::HeaderValue, header::ACCEPT,
5  header::AUTHORIZATION, header::CONTENT_TYPE,
6};
7use std::default::Default;
8
9pub const HOSTNAME: &str = "https://platform.clickatell.com";
10const MESSAGE_PATH: &str = "/v1/message";
11const BALANCE_PATH: &str = "/v1/balance";
12const APPLICATION_JSON: &str = "application/json";
13
14/// Clickatell One messaging gateway client
15pub struct BlockingClient {
16  hostname: String,
17  http_client: HTTPClient,
18}
19
20impl BlockingClient {
21  pub fn new(api_key: &str) -> Result<Self> {
22    Self::builder().api_key(api_key).build()
23  }
24
25  pub fn builder() -> BlockingClientBuilder {
26    BlockingClientBuilder::default()
27  }
28
29  /// Send messages via the gateway
30  ///
31  /// A return of `Ok` does not mean the messages have been successfully delivered, only that
32  /// the messages have been accepted by the gateway. Message status can be queried with
33  /// [message_status].
34  ///
35  /// ```rust,ignore
36  /// let mut request = send_messages::Request::new();
37  /// request.add_message(Channel::SMS, to, "This is an SMS message");
38  /// request.add_message(Channel::WhatsApp, to, "This is a WhatsApp message");
39  ///
40  /// let response = client.send_messages(request).await?;
41  /// for message_response in response.messages() {
42  ///     println!("{} {}", message_response.to, message_response.message_id());
43  /// }
44  /// ```
45  pub fn send_messages(
46    &self,
47    send_messages_request: send_messages::Request,
48  ) -> Result<send_messages::Response> {
49    if send_messages_request.message_count() >= 100 {
50      return Err(Error::TooManyMessages);
51    }
52
53    let response = self
54      .http_client
55      .post(format!("{}{MESSAGE_PATH}", self.hostname))
56      .json(&send_messages_request)
57      .send()?;
58
59    Ok(response.json::<send_messages::Response>()?)
60  }
61
62  /// Query the delivery status of a message
63  ///
64  /// ```rust,ignore
65  /// let request = message_status::Request::new(message_id);
66  /// let status_response = client.message_status(request).await?;
67  /// println!("Status: {}", status_response.status())
68  /// ```
69  pub fn message_status(
70    &self,
71    request: message_status::Request,
72  ) -> Result<message_status::Response> {
73    let response = self
74      .http_client
75      .get(format!("{}{MESSAGE_PATH}/{}", self.hostname, request))
76      .send()?;
77
78    Ok(response.json::<message_status::Response>()?)
79  }
80
81  /// Return the balance of your Clickatell account
82  ///
83  /// ```rust,ignore
84  /// let balance_response = client.balance().await?;
85  /// println!("Your balance is {} {}", balance_response.currency, balance_response.balance);
86  /// ```
87  pub fn balance(&self) -> Result<balance::Response> {
88    let response = self
89      .http_client
90      .get(format!("{}{BALANCE_PATH}", self.hostname))
91      .send()?;
92
93    Ok(response.json::<balance::Response>()?)
94  }
95}
96
97/// Used to create a [Client] that can point to a different URL than the default Clickatell One
98/// gateway
99pub struct BlockingClientBuilder {
100  hostname: Option<String>,
101  api_key: Option<String>,
102}
103
104impl Default for BlockingClientBuilder {
105  fn default() -> Self {
106    BlockingClientBuilder {
107      hostname: Some(HOSTNAME.to_string()),
108      api_key: None,
109    }
110  }
111}
112
113impl BlockingClientBuilder {
114  pub fn api_key(mut self, api_key: &str) -> Self {
115    self.api_key = Some(api_key.to_string());
116    self
117  }
118
119  pub fn hostname(mut self, hostname: &str) -> Self {
120    self.hostname = Some(hostname.to_string());
121    self
122  }
123
124  pub fn build(self) -> Result<BlockingClient> {
125    match (self.api_key, self.hostname) {
126      (Some(api_key), Some(hostname)) => {
127        let mut headers = HeaderMap::new();
128
129        let mut auth_value = HeaderValue::from_str(&api_key)?;
130        auth_value.set_sensitive(true);
131
132        headers.insert(AUTHORIZATION, auth_value);
133        headers.insert(CONTENT_TYPE, HeaderValue::from_str(APPLICATION_JSON)?);
134        headers.insert(ACCEPT, HeaderValue::from_str(APPLICATION_JSON)?);
135
136        let http_client = HTTPClient::builder().default_headers(headers).build()?;
137
138        Ok(BlockingClient {
139          hostname,
140          http_client,
141        })
142      }
143      (None, _) => Err(Error::ApiKeyNotSet),
144      (Some(_), None) => Err(Error::HostnameNotSet),
145    }
146  }
147}