aa_sms/
message.rs

1/// A `Message` to send with a [`Client`][crate::Client].
2///
3/// To configure a `Message`, use [`Message::builder()`].
4#[derive(serde::Serialize)]
5pub struct Message {
6    da: String,
7    ud: String,
8    #[serde(skip_serializing_if = "Option::is_none")]
9    limit: Option<u8>,
10    #[serde(skip_serializing_if = "Option::is_none")]
11    costcentre: Option<String>,
12    #[serde(skip_serializing_if = "Option::is_none")]
13    private: Option<bool>,
14    #[serde(skip_serializing_if = "Option::is_none")]
15    oa: Option<String>,
16    #[serde(skip_serializing_if = "Option::is_none")]
17    udh: Option<String>,
18    #[serde(skip_serializing_if = "Option::is_none")]
19    srr: Option<String>,
20}
21
22impl Message {
23    /// Creates a `MessageBuilder` to configure a `Message`.
24    pub fn builder() -> MessageBuilder {
25        MessageBuilder {
26            ..MessageBuilder::default()
27        }
28    }
29}
30
31/// A builder to construct the properties of a [`Message`].
32#[derive(Default, Debug)]
33pub struct MessageBuilder {
34    da: Option<String>,
35    ud: Option<String>,
36    limit: Option<u8>,
37    costcentre: Option<String>,
38    private: Option<bool>,
39    oa: Option<String>,
40    udh: Option<String>,
41    srr: Option<String>,
42}
43
44impl MessageBuilder {
45    /// Returns a `Message` that uses this `MessageBuilder` configuration.
46    pub fn build(self) -> Message {
47        Message {
48            da: self
49                .da
50                .expect("Cannot build sms without destination, `da`."),
51            ud: self.ud.expect("Cannot build sms without message, `ud`."),
52            limit: self.limit,
53            costcentre: self.costcentre,
54            private: self.private,
55            oa: self.oa,
56            udh: self.udh,
57            srr: self.srr,
58        }
59    }
60
61    /// Sets the `da`, or Destination Address, for the `Message`.
62    ///
63    /// This is the number to which the message is to be sent and should be a
64    /// full international format number (however, national format is also
65    /// accepted). This may be a SIP2SIM ICCID to send direct to a SIM.
66    ///
67    /// This is the same as `MessageBuilder::destination()`
68    pub fn da(mut self, da: impl Into<String>) -> Self {
69        self.da = Some(da.into());
70        self
71    }
72
73    /// Sets the `da`, or Destination Address, for the `Message`.
74    ///
75    /// This is the same as `MessageBuilder::da()`
76    pub fn destination(self, destination: impl Into<String>) -> Self {
77        self.da(destination)
78    }
79
80    /// Sets the `ud`, or User Data ie. the text body, for the `Message`.
81    ///
82    /// This is the message to send, encoded in UTF-8.
83    ///
84    /// This is the same as `MessageBuilder::message()`
85    pub fn ud(mut self, ud: impl Into<String>) -> Self {
86        self.ud = Some(ud.into());
87        self
88    }
89
90    /// Sets the `ud`, or User Data ie. the text body, for the `Message`.
91    ///
92    /// This is the same as `MessageBuilder::ud()`
93    pub fn message(self, message: impl Into<String>) -> Self {
94        self.ud(message)
95    }
96
97    /// Sets the `limit` for the `Message`.
98    ///
99    /// This is the limit on the number of parts that the message may be sent
100    /// in.
101    pub fn limit(mut self, limit: u8) -> Self {
102        self.limit = Some(limit);
103        self
104    }
105
106    /// Sets the `costcentre` for the `Message`.
107    ///
108    /// This is the optional, up to 10 characters, code that is included in the
109    /// bill XML data.
110    pub fn costcentre(mut self, costcentre: impl Into<String>) -> Self {
111        self.costcentre = Some(costcentre.into());
112        self
113    }
114
115    /// Sets the `private` setting for the `Message`.
116    ///
117    /// This marks the message as private, such that it is not included in the
118    /// itemised billing.
119    pub fn private(mut self, private: bool) -> Self {
120        self.private = Some(private);
121        self
122    }
123
124    /// Sets the `oa`, or originator, for the `Message`.
125    ///
126    /// This is used to set where the message is from. Normally this is not
127    /// needed as it will default to your `username` phone number.
128    pub fn oa(mut self, oa: impl Into<String>) -> Self {
129        self.oa = Some(oa.into());
130        self
131    }
132
133    /// Sets the `udh`, or User Data Header, for the `Message`.
134    ///
135    /// This is a hex-coded GSM 03.40 UDH.
136    pub fn udh(mut self, udh: impl Into<String>) -> Self {
137        self.udh = Some(udh.into());
138        self
139    }
140
141    /// Sets the `srr`, or SMS Status Report, for the `Message`.
142    ///
143    /// This is the URL or email address to send delivery reports.
144    pub fn srr(mut self, srr: impl Into<String>) -> Self {
145        self.srr = Some(srr.into());
146        self
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153
154    #[test]
155    fn builder_default_all_none() {
156        let mb = Message::builder();
157        assert_eq!(mb.da, None);
158        assert_eq!(mb.ud, None);
159        assert_eq!(mb.limit, None);
160        assert_eq!(mb.costcentre, None);
161        assert_eq!(mb.private, None);
162        assert_eq!(mb.oa, None);
163        assert_eq!(mb.udh, None);
164        assert_eq!(mb.srr, None);
165    }
166
167    #[test]
168    fn basic_builder_setters_work() {
169        let mb = Message::builder().da("123");
170        assert_eq!(mb.da, Some(String::from("123")));
171
172        let mb = Message::builder().ud("123");
173        assert_eq!(mb.ud, Some(String::from("123")));
174        assert_eq!(mb.da, None);
175
176        let mb = Message::builder().limit(1);
177        assert_eq!(mb.limit, Some(1));
178        assert_eq!(mb.ud, None);
179
180        let mb = Message::builder().costcentre("123");
181        assert_eq!(mb.costcentre, Some(String::from("123")));
182        assert_eq!(mb.limit, None);
183
184        let mb = Message::builder().private(true);
185        assert_eq!(mb.private, Some(true));
186        assert_eq!(mb.costcentre, None);
187
188        let mb = Message::builder().oa("123");
189        assert_eq!(mb.oa, Some(String::from("123")));
190        assert_eq!(mb.private, None);
191
192        let mb = Message::builder().udh("123");
193        assert_eq!(mb.udh, Some(String::from("123")));
194        assert_eq!(mb.oa, None);
195
196        let mb = Message::builder().srr("123");
197        assert_eq!(mb.srr, Some(String::from("123")));
198        assert_eq!(mb.udh, None);
199    }
200
201    #[test]
202    fn basic_builder_works() {
203        let message = Message::builder().da("123").ud("456").build();
204        assert_eq!(message.da, "123");
205        assert_eq!(message.ud, "456");
206    }
207
208    #[test]
209    fn sugar_builder_works() {
210        let message = Message::builder().destination("123").message("456").build();
211        assert_eq!(message.da, "123");
212        assert_eq!(message.ud, "456");
213    }
214
215    #[test]
216    fn basic_builder_order_doesnt_matter() {
217        let message1 = Message::builder().da("123").ud("456").build();
218        let message2 = Message::builder().ud("456").da("123").build();
219        assert_eq!(message1.da, message2.da);
220        assert_eq!(message1.ud, message2.ud);
221    }
222
223    #[test]
224    fn sugar_or_basic_builder_doesnt_matter() {
225        let message1 = Message::builder().da("123").message("456").build();
226        let message2 = Message::builder().destination("123").ud("456").build();
227        assert_eq!(message1.da, message2.da);
228        assert_eq!(message1.ud, message2.ud);
229    }
230
231    #[test]
232    #[should_panic]
233    fn build_without_da_fails() {
234        Message::builder().ud("123").build();
235    }
236
237    #[test]
238    #[should_panic]
239    fn build_without_ud_fails() {
240        Message::builder().da("123").build();
241    }
242}