aws_iot_device_sdk_embedded_rust/
jobs.rs1use crate::common::*;
2use arrayvec::{ArrayString, ArrayVec};
3
4use self::Topic::*;
5
6const API_BRIDGE: &str = "/jobs/";
7const API_JOBSCHANGED: &str = "notify";
8const API_NEXTJOBCHANGED: &str = "notify-next";
9const API_GETPENDING: &str = "get";
10const API_STARTNEXT: &str = "start-next";
11const API_DESCRIBE: &str = "get";
12const API_UPDATE: &str = "update";
13const API_JOBID_NEXT: &str = "$next";
14
15pub struct ThingJobs<'a> {
18 pub thing_name: &'a str,
19 pub api: Topic,
20 pub id: Option<ArrayString<JOBID_MAX_LENGTH>>,
21}
22
23#[derive(Debug, PartialEq, PartialOrd)]
27pub enum Topic {
28 JobsChanged,
29 NextJobChanged,
30 GetPendingSuccess,
31 GetPendingFailed,
32 StartNextSuccess,
33 StartNextFailed,
34 DescribeSuccess,
36 DescribeFailed,
37 UpdateSuccess,
38 UpdateFailed,
39}
40
41pub fn get_topic(
53 thing_name: &str,
54 api: Topic,
55) -> Result<ArrayString<JOBS_TOPIC_MAX_LENGTH>, Error> {
56 is_valid_thing_name(thing_name)?;
57 let mut s = ArrayString::<JOBS_TOPIC_MAX_LENGTH>::new();
58 s.push_str(AWS_THINGS_PREFIX);
59 s.push_str(thing_name);
60 s.push_str(API_BRIDGE);
61 s.push_str(id(&api));
62 s.push_str(op(&api));
63 s.push_str(suffix(&api));
64
65 Ok(s)
66}
67
68fn id(api: &Topic) -> &str {
69 match api {
70 DescribeSuccess | DescribeFailed | UpdateSuccess | UpdateFailed => "+/",
71 _ => "",
72 }
73}
74
75fn op(api: &Topic) -> &str {
76 match api {
77 JobsChanged => API_JOBSCHANGED,
78 NextJobChanged => API_NEXTJOBCHANGED,
79 GetPendingSuccess => API_GETPENDING,
80 GetPendingFailed => API_GETPENDING,
81 StartNextSuccess => API_STARTNEXT,
82 StartNextFailed => API_STARTNEXT,
83 DescribeSuccess => API_DESCRIBE,
85 DescribeFailed => API_DESCRIBE,
86 UpdateSuccess => API_UPDATE,
87 UpdateFailed => API_UPDATE,
88 }
89}
90
91fn suffix(topic_type: &Topic) -> &str {
92 match topic_type {
93 GetPendingSuccess | StartNextSuccess | DescribeSuccess | UpdateSuccess => SUFFIX_ACCEPTED,
94 GetPendingFailed | StartNextFailed | DescribeFailed | UpdateFailed => SUFFIX_REJECTED,
95 _ => "",
96 }
97}
98pub fn match_topic(topic: &str) -> Result<ThingJobs, Error> {
113 is_valid_mqtt_topic(topic)?;
114
115 let s = is_valid_prefix(topic, AWS_THINGS_PREFIX)?;
116
117 let mid = s.find('/').ok_or(Error::FAIL);
118 let (thing_name, mut s) = s.split_at(mid?);
119 is_valid_thing_name(thing_name)?;
120
121 s = is_valid_bridge(s, API_BRIDGE)?;
122
123 let v: ArrayVec<&str, 16> = s.split('/').collect();
124 let api: Topic;
125 let jobs_id;
126 match v[..] {
127 [op] => {
130 if op == API_JOBSCHANGED {
131 api = JobsChanged;
132 } else {
133 api = NextJobChanged;
134 }
135 Ok(ThingJobs {
136 thing_name,
137 api,
138 id: None,
139 })
140 }
141 [op, suffix] => {
143 match (op, suffix) {
144 (API_GETPENDING, ACCEPTED) => api = GetPendingSuccess,
145 (API_GETPENDING, REJECTED) => api = GetPendingFailed,
146 (API_STARTNEXT, ACCEPTED) => api = StartNextSuccess,
147 (API_STARTNEXT, REJECTED) => api = StartNextFailed,
148 _ => return Err(Error::NoMatch),
149 }
150 Ok(ThingJobs {
151 thing_name,
152 api,
153 id: None,
154 })
155 }
156 [id, op, suffix] => {
158 match (op, suffix) {
159 (API_DESCRIBE, ACCEPTED) => api = DescribeSuccess,
160 (API_DESCRIBE, REJECTED) => api = DescribeFailed,
161 (API_UPDATE, ACCEPTED) => api = UpdateSuccess,
162 (API_UPDATE, REJECTED) => api = UpdateFailed,
163 _ => return Err(Error::NoMatch),
164 }
165 jobs_id = Some(ArrayString::<JOBID_MAX_LENGTH>::from(id).unwrap());
166 Ok(ThingJobs {
167 thing_name,
168 api,
169 id: jobs_id,
170 })
171 }
172 _ => Err(Error::NoMatch),
174 }
175}
176pub fn get_pending(thing_name: &str) -> Result<ArrayString<THINGNAME_MAX_LENGTH>, Error> {
179 is_valid_thing_name(thing_name)?;
180 let mut s = ArrayString::<THINGNAME_MAX_LENGTH>::new();
181 s.push_str(AWS_THINGS_PREFIX);
182 s.push_str(thing_name);
183 s.push_str(API_BRIDGE);
184 s.push_str(API_GETPENDING);
185
186 Ok(s)
187}
188pub fn start_next(thing_name: &str) -> Result<ArrayString<THINGNAME_MAX_LENGTH>, Error> {
191 is_valid_thing_name(thing_name)?;
192 let mut s = ArrayString::<THINGNAME_MAX_LENGTH>::new();
193 s.push_str(AWS_THINGS_PREFIX);
194 s.push_str(thing_name);
195 s.push_str(API_BRIDGE);
196 s.push_str(API_STARTNEXT);
197
198 Ok(s)
199}
200pub fn describe(thing_name: &str, id: &str) -> Result<ArrayString<THINGNAME_MAX_LENGTH>, Error> {
212 is_valid_thing_name(thing_name)?;
213 if id != API_JOBID_NEXT {
214 is_valid_job_id(id)?
215 };
216 let mut s = ArrayString::<THINGNAME_MAX_LENGTH>::new();
217 s.push_str(AWS_THINGS_PREFIX);
218 s.push_str(thing_name);
219 s.push_str(API_BRIDGE);
220 s.push_str(id);
221 s.push_str("/");
222 s.push_str(API_DESCRIBE);
223
224 Ok(s)
225}
226pub fn update(thing_name: &str, id: &str) -> Result<ArrayString<THINGNAME_MAX_LENGTH>, Error> {
229 is_valid_thing_name(thing_name)?;
230 is_valid_job_id(id)?;
231 let mut s = ArrayString::<THINGNAME_MAX_LENGTH>::new();
232 s.push_str(AWS_THINGS_PREFIX);
233 s.push_str(thing_name);
234 s.push_str(API_BRIDGE);
235 s.push_str(id);
236 s.push_str("/");
237 s.push_str(API_UPDATE);
238
239 Ok(s)
240}
241
242#[cfg(test)]
243mod tests {
244 use crate::jobs;
245 #[test]
246 fn get_topic_notify_next() {
247 let topic = jobs::get_topic("chloe", jobs::Topic::NextJobChanged).unwrap();
248 assert_eq!(&topic[..], "$aws/things/chloe/jobs/notify-next");
249 }
250
251 #[test]
252 fn get_topic_get_rejected() {
253 let topic = jobs::get_topic("chloe", jobs::Topic::GetPendingFailed).unwrap();
254 assert_eq!(&topic[..], "$aws/things/chloe/jobs/get/rejected");
255 }
256
257 #[test]
258 fn get_topic_id_update_rejected() {
259 let topic = jobs::get_topic("chloe", jobs::Topic::UpdateFailed).unwrap();
260 assert_eq!(&topic[..], "$aws/things/chloe/jobs/+/update/rejected");
261 }
262
263 #[test]
264 fn match_topic() {
265 let jobs = jobs::match_topic("$aws/things/chloe/jobs/notify-next").unwrap();
266 assert_eq!(jobs.api, jobs::Topic::NextJobChanged);
267 assert_eq!(jobs.id, None);
268 }
269 #[test]
270 fn match_topic_with_op() {
271 let jobs = jobs::match_topic("$aws/things/chloe/jobs/get/rejected").unwrap();
272 assert_eq!(jobs.api, jobs::Topic::GetPendingFailed);
273 assert_eq!(jobs.id, None);
274 }
275 #[test]
276 fn match_topic_with_id_op() {
277 let jobs = jobs::match_topic("$aws/things/chloe/jobs/example-job-01/get/accepted").unwrap();
278 assert_eq!(jobs.api, jobs::Topic::DescribeSuccess);
279 let id = jobs.id.unwrap();
280 assert_eq!(&id[..], "example-job-01");
281 }
282 #[test]
283 fn get_pending() {
284 let topic = jobs::get_pending("chloe").unwrap();
285 assert_eq!(&topic[..], "$aws/things/chloe/jobs/get");
286 }
287 #[test]
288 fn start_next() {
289 let topic = jobs::start_next("chloe").unwrap();
290 assert_eq!(&topic[..], "$aws/things/chloe/jobs/start-next");
291 }
292 #[test]
293 fn update() {
294 let topic = jobs::update("chloe", "example-job-01").unwrap();
295 assert_eq!(&topic[..], "$aws/things/chloe/jobs/example-job-01/update");
296 }
297}