use arrayvec::{ArrayString, ArrayVec};
use self::Topic::*;
use crate::common::*;
const API_JSON_FORMAT: &str = "json";
const API_CBOR_FORMAT: &str = "cbor";
pub struct ThingDefender<'a> {
pub thing_name: &'a str,
pub api: Topic,
}
#[derive(Debug, PartialEq)]
pub enum Topic {
JsonReportPublish,
JsonReportAccepted,
JsonReportRejected,
CborReportPublish,
CborReportAccepted,
CborReportRejected,
}
pub fn assemble_topic(
thing_name: &str,
api: Topic,
) -> Result<ArrayString<DEFENDER_TOPIC_MAX_LENGTH>, Error> {
is_valid_thing_name(thing_name)?;
let mut s = ArrayString::<DEFENDER_TOPIC_MAX_LENGTH>::new();
s.push_str(AWS_THINGS_PREFIX);
s.push_str(thing_name);
s.push_str(DEFENDER_API_BRIDGE);
s.push_str(op(&api));
s.push_str(suffix(&api));
Ok(s)
}
fn op(api: &Topic) -> &str {
match api {
JsonReportPublish | JsonReportAccepted | JsonReportRejected => API_JSON_FORMAT,
CborReportPublish | CborReportAccepted | CborReportRejected => API_CBOR_FORMAT,
}
}
fn suffix(topic_type: &Topic) -> &str {
match topic_type {
JsonReportAccepted | CborReportAccepted => SUFFIX_ACCEPTED,
JsonReportRejected | CborReportRejected => SUFFIX_REJECTED,
_ => "",
}
}
pub fn match_topic(topic: &str) -> Result<ThingDefender, Error> {
is_valid_mqtt_topic(topic)?;
let s = is_valid_prefix(topic, AWS_THINGS_PREFIX)?;
let mid = s.find('/').ok_or(Error::FAIL);
let (thing_name, mut s) = s.split_at(mid?);
is_valid_thing_name(thing_name)?;
s = is_valid_bridge(s, DEFENDER_API_BRIDGE)?;
let v: ArrayVec<&str, 16> = s.split('/').collect();
let api: Topic;
match v[..] {
[op, suffix] => {
match (op, suffix) {
(API_JSON_FORMAT, ACCEPTED) => api = JsonReportAccepted,
(API_JSON_FORMAT, REJECTED) => api = JsonReportRejected,
(API_CBOR_FORMAT, ACCEPTED) => api = CborReportAccepted,
(API_CBOR_FORMAT, REJECTED) => api = CborReportRejected,
_ => return Err(Error::NoMatch),
}
Ok(ThingDefender { thing_name, api })
}
_ => Err(Error::NoMatch),
}
}
#[cfg(test)]
mod tests {
use crate::defender;
#[test]
fn assemble_topic_json() {
let topic = defender::assemble_topic("chloe", defender::Topic::JsonReportPublish).unwrap();
assert_eq!(&topic[..], "$aws/things/chloe/defender/metrics/json");
}
#[test]
fn assemble_topic_cbor_rejected() {
let topic = defender::assemble_topic("chloe", defender::Topic::CborReportRejected).unwrap();
assert_eq!(
&topic[..],
"$aws/things/chloe/defender/metrics/cbor/rejected"
);
}
#[test]
fn test_match_topic_some_name() {
let defender =
defender::match_topic("$aws/things/chloe/defender/metrics/json/accepted").unwrap();
assert_eq!(defender.thing_name, "chloe");
assert_eq!(defender.api, defender::Topic::JsonReportAccepted);
}
}