use crate::transport::mqtt::topic::Topic;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
use thiserror::Error;
#[derive(Error, Debug, PartialEq)]
pub enum StrTopicError {
#[error("Cannot update topic at level 0")]
LevelZero,
#[error("Cannot update topic at level {0}: level is too high")]
LevelTooHigh(u8),
}
#[derive(Clone, Default, Debug, Hash, PartialEq, Eq)]
pub struct StrTopic {
topic: String,
}
impl StrTopic {
pub fn parts(&self) -> Vec<&str> {
self.topic.split('/').collect()
}
pub fn replace_at(&mut self, level: u8, value: &str) -> Result<(), StrTopicError> {
if level == 0 {
return Err(StrTopicError::LevelZero);
}
let mut parts = self.parts();
if level as usize <= parts.len() {
parts[level as usize - 1] = value;
self.topic = parts.join("/");
Ok(())
} else {
Err(StrTopicError::LevelTooHigh(level))
}
}
}
impl Display for StrTopic {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.topic)
}
}
impl FromStr for StrTopic {
type Err = std::str::Utf8Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(StrTopic {
topic: String::from(s),
})
}
}
impl Topic for StrTopic {
fn as_route(&self) -> String {
self.topic.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
fn create_str_topic(topic: &str) -> StrTopic {
StrTopic {
topic: topic.to_string(),
}
}
#[test]
fn str_topic_display() {
let topic = create_str_topic("test/topic");
assert_eq!(format!("{topic}"), "test/topic");
}
#[test]
fn str_topic_from_str_valid() {
let topic = StrTopic::from_str("test/topic").unwrap();
assert_eq!(topic, create_str_topic("test/topic"));
}
#[test]
fn str_topic_from_str_empty() {
let topic = StrTopic::from_str("").unwrap();
assert_eq!(topic, create_str_topic(""));
}
#[test]
fn str_topic_as_route() {
let topic = create_str_topic("test/route");
assert_eq!(topic.as_route(), "test/route");
}
#[test]
fn str_topic_not_replace_at_at_level_0() {
let mut topic = create_str_topic("a/b/c/d");
let result = topic.replace_at(0, "x");
assert!(result.is_err());
assert_eq!(result.unwrap_err(), StrTopicError::LevelZero);
assert_eq!(topic.to_string(), "a/b/c/d");
}
#[test]
fn str_topic_replace_at_at_first_level() {
let mut topic = create_str_topic("a/b/c/d");
let result = topic.replace_at(1, "x");
assert!(result.is_ok());
assert_eq!(topic.to_string(), "x/b/c/d");
}
#[test]
fn str_topic_replace_at_at_level_2() {
let mut topic = create_str_topic("a/b/c/d");
let result = topic.replace_at(2, "x");
assert!(result.is_ok());
assert_eq!(topic.to_string(), "a/x/c/d");
}
#[test]
fn str_topic_replace_at_at_level_3() {
let mut topic = create_str_topic("a/b/c/d");
let result = topic.replace_at(3, "x");
assert!(result.is_ok());
assert_eq!(topic.to_string(), "a/b/x/d");
}
#[test]
fn str_topic_replace_at_at_last_level() {
let mut topic = create_str_topic("a/b/c/d");
let result = topic.replace_at(4, "x");
assert!(result.is_ok());
assert_eq!(topic.to_string(), "a/b/c/x");
}
#[test]
fn str_topic_not_replace_at_at_level_too_high() {
let mut topic = create_str_topic("a/b/c/d");
let result = topic.replace_at(5, "x");
assert!(result.is_err());
assert_eq!(result.unwrap_err(), StrTopicError::LevelTooHigh(5));
assert_eq!(topic.to_string(), "a/b/c/d");
}
}