aws_iot_device_sdk_embedded_rust/
defender.rs1use arrayvec::{ArrayString, ArrayVec};
2
3use self::Topic::*;
4use crate::common::*;
5
6const API_BRIDGE: &str = "/defender/metrics/";
7const API_JSON_FORMAT: &str = "json";
8const API_CBOR_FORMAT: &str = "cbor";
9
10pub struct ThingDefender<'a> {
13 pub thing_name: &'a str,
14 pub api: Topic,
15}
16
17#[derive(Debug, PartialEq)]
28pub enum Topic {
29 JsonReportPublish,
30 JsonReportAccepted,
32 JsonReportRejected,
34 CborReportPublish,
36 CborReportAccepted,
38 CborReportRejected, }
41
42pub fn get_topic(
53 thing_name: &str,
54 api: Topic,
55) -> Result<ArrayString<DEFENDER_TOPIC_MAX_LENGTH>, Error> {
56 is_valid_thing_name(thing_name)?;
57 let mut s = ArrayString::<DEFENDER_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(op(&api));
62 s.push_str(suffix(&api));
63
64 Ok(s)
65}
66
67fn op(api: &Topic) -> &str {
68 match api {
69 JsonReportPublish | JsonReportAccepted | JsonReportRejected => API_JSON_FORMAT,
70 CborReportPublish | CborReportAccepted | CborReportRejected => API_CBOR_FORMAT,
71 }
72}
73
74fn suffix(topic_type: &Topic) -> &str {
75 match topic_type {
76 JsonReportAccepted | CborReportAccepted => SUFFIX_ACCEPTED,
77 JsonReportRejected | CborReportRejected => SUFFIX_REJECTED,
78 _ => "",
79 }
80}
81
82pub fn match_topic(topic: &str) -> Result<ThingDefender, Error> {
96 is_valid_mqtt_topic(topic)?;
97
98 let s = is_valid_prefix(topic, AWS_THINGS_PREFIX)?;
99
100 let mid = s.find('/').ok_or(Error::FAIL);
101 let (thing_name, mut s) = s.split_at(mid?);
102 is_valid_thing_name(thing_name)?;
103
104 s = is_valid_bridge(s, API_BRIDGE)?;
105
106 let v: ArrayVec<&str, 16> = s.split('/').collect();
107 let api: Topic;
108 match v[..] {
109 [op, suffix] => {
111 match (op, suffix) {
112 (API_JSON_FORMAT, ACCEPTED) => api = JsonReportAccepted,
113 (API_JSON_FORMAT, REJECTED) => api = JsonReportRejected,
114 (API_CBOR_FORMAT, ACCEPTED) => api = CborReportAccepted,
115 (API_CBOR_FORMAT, REJECTED) => api = CborReportRejected,
116 _ => return Err(Error::NoMatch),
117 }
118 Ok(ThingDefender { thing_name, api })
119 }
120 _ => Err(Error::NoMatch),
122 }
123}
124#[cfg(test)]
125mod tests {
126 use crate::defender;
127 #[test]
128 fn get_topic_json() {
129 let topic = defender::get_topic("chloe", defender::Topic::JsonReportPublish).unwrap();
130 assert_eq!(&topic[..], "$aws/things/chloe/defender/metrics/json");
131 }
132
133 #[test]
134 fn get_topic_cbor_rejected() {
135 let topic = defender::get_topic("chloe", defender::Topic::CborReportRejected).unwrap();
136 assert_eq!(
137 &topic[..],
138 "$aws/things/chloe/defender/metrics/cbor/rejected"
139 );
140 }
141 #[test]
142 fn test_match_topic_some_name() {
143 let defender =
144 defender::match_topic("$aws/things/chloe/defender/metrics/json/accepted").unwrap();
145
146 assert_eq!(defender.thing_name, "chloe");
147 assert_eq!(defender.api, defender::Topic::JsonReportAccepted);
148 }
149}