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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/// A `Message` to send with a [`Client`][crate::Client].
///
/// To configure a `Message`, use [`Message::builder()`].
#[derive(serde::Serialize)]
pub struct Message {
    da: String,
    ud: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    limit: Option<u8>,
    #[serde(skip_serializing_if = "Option::is_none")]
    costcentre: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    private: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    oa: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    udh: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    srr: Option<String>,
}

impl Message {
    /// Creates a `MessageBuilder` to configure a `Message`.
    pub fn builder() -> MessageBuilder {
        MessageBuilder {
            ..MessageBuilder::default()
        }
    }
}

/// A builder to construct the properties of a [`Message`].
#[derive(Default, Debug)]
pub struct MessageBuilder {
    da: Option<String>,
    ud: Option<String>,
    limit: Option<u8>,
    costcentre: Option<String>,
    private: Option<bool>,
    oa: Option<String>,
    udh: Option<String>,
    srr: Option<String>,
}

impl MessageBuilder {
    /// Returns a `Message` that uses this `MessageBuilder` configuration.
    pub fn build(self) -> Message {
        Message {
            da: self
                .da
                .expect("Cannot build sms without destination, `da`."),
            ud: self.ud.expect("Cannot build sms without message, `ud`."),
            limit: self.limit,
            costcentre: self.costcentre,
            private: self.private,
            oa: self.oa,
            udh: self.udh,
            srr: self.srr,
        }
    }

    /// Sets the `da`, or Destination Address, for the `Message`.
    ///
    /// This is the number to which the message is to be sent and should be a
    /// full international format number (however, national format is also
    /// accepted). This may be a SIP2SIM ICCID to send direct to a SIM.
    ///
    /// This is the same as `MessageBuilder::destination()`
    pub fn da(mut self, da: impl Into<String>) -> Self {
        self.da = Some(da.into());
        self
    }

    /// Sets the `da`, or Destination Address, for the `Message`.
    ///
    /// This is the same as `MessageBuilder::da()`
    pub fn destination(self, destination: impl Into<String>) -> Self {
        self.da(destination)
    }

    /// Sets the `ud`, or User Data ie. the text body, for the `Message`.
    ///
    /// This is the message to send, encoded in UTF-8.
    ///
    /// This is the same as `MessageBuilder::message()`
    pub fn ud(mut self, ud: impl Into<String>) -> Self {
        self.ud = Some(ud.into());
        self
    }

    /// Sets the `ud`, or User Data ie. the text body, for the `Message`.
    ///
    /// This is the same as `MessageBuilder::ud()`
    pub fn message(self, message: impl Into<String>) -> Self {
        self.ud(message)
    }

    /// Sets the `limit` for the `Message`.
    ///
    /// This is the limit on the number of parts that the message may be sent
    /// in.
    pub fn limit(mut self, limit: u8) -> Self {
        self.limit = Some(limit);
        self
    }

    /// Sets the `costcentre` for the `Message`.
    ///
    /// This is the optional, up to 10 characters, code that is included in the
    /// bill XML data.
    pub fn costcentre(mut self, costcentre: impl Into<String>) -> Self {
        self.costcentre = Some(costcentre.into());
        self
    }

    /// Sets the `private` setting for the `Message`.
    ///
    /// This marks the message as private, such that it is not included in the
    /// itemised billing.
    pub fn private(mut self, private: bool) -> Self {
        self.private = Some(private);
        self
    }

    /// Sets the `oa`, or originator, for the `Message`.
    ///
    /// This is used to set where the message is from. Normally this is not
    /// needed as it will default to your `username` phone number.
    pub fn oa(mut self, oa: impl Into<String>) -> Self {
        self.oa = Some(oa.into());
        self
    }

    /// Sets the `udh`, or User Data Header, for the `Message`.
    ///
    /// This is a hex-coded GSM 03.40 UDH.
    pub fn udh(mut self, udh: impl Into<String>) -> Self {
        self.udh = Some(udh.into());
        self
    }

    /// Sets the `srr`, or SMS Status Report, for the `Message`.
    ///
    /// This is the URL or email address to send delivery reports.
    pub fn srr(mut self, srr: impl Into<String>) -> Self {
        self.srr = Some(srr.into());
        self
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn builder_default_all_none() {
        let mb = Message::builder();
        assert_eq!(mb.da, None);
        assert_eq!(mb.ud, None);
        assert_eq!(mb.limit, None);
        assert_eq!(mb.costcentre, None);
        assert_eq!(mb.private, None);
        assert_eq!(mb.oa, None);
        assert_eq!(mb.udh, None);
        assert_eq!(mb.srr, None);
    }

    #[test]
    fn basic_builder_setters_work() {
        let mb = Message::builder().da("123");
        assert_eq!(mb.da, Some(String::from("123")));

        let mb = Message::builder().ud("123");
        assert_eq!(mb.ud, Some(String::from("123")));
        assert_eq!(mb.da, None);

        let mb = Message::builder().limit(1);
        assert_eq!(mb.limit, Some(1));
        assert_eq!(mb.ud, None);

        let mb = Message::builder().costcentre("123");
        assert_eq!(mb.costcentre, Some(String::from("123")));
        assert_eq!(mb.limit, None);

        let mb = Message::builder().private(true);
        assert_eq!(mb.private, Some(true));
        assert_eq!(mb.costcentre, None);

        let mb = Message::builder().oa("123");
        assert_eq!(mb.oa, Some(String::from("123")));
        assert_eq!(mb.private, None);

        let mb = Message::builder().udh("123");
        assert_eq!(mb.udh, Some(String::from("123")));
        assert_eq!(mb.oa, None);

        let mb = Message::builder().srr("123");
        assert_eq!(mb.srr, Some(String::from("123")));
        assert_eq!(mb.udh, None);
    }

    #[test]
    fn basic_builder_works() {
        let message = Message::builder().da("123").ud("456").build();
        assert_eq!(message.da, "123");
        assert_eq!(message.ud, "456");
    }

    #[test]
    fn sugar_builder_works() {
        let message = Message::builder().destination("123").message("456").build();
        assert_eq!(message.da, "123");
        assert_eq!(message.ud, "456");
    }

    #[test]
    fn basic_builder_order_doesnt_matter() {
        let message1 = Message::builder().da("123").ud("456").build();
        let message2 = Message::builder().ud("456").da("123").build();
        assert_eq!(message1.da, message2.da);
        assert_eq!(message1.ud, message2.ud);
    }

    #[test]
    fn sugar_or_basic_builder_doesnt_matter() {
        let message1 = Message::builder().da("123").message("456").build();
        let message2 = Message::builder().destination("123").ud("456").build();
        assert_eq!(message1.da, message2.da);
        assert_eq!(message1.ud, message2.ud);
    }

    #[test]
    #[should_panic]
    fn build_without_da_fails() {
        Message::builder().ud("123").build();
    }

    #[test]
    #[should_panic]
    fn build_without_ud_fails() {
        Message::builder().da("123").build();
    }
}