#[inline]
pub fn len_bytes(len: usize) -> usize {
if len < 128 {
1
} else if len < 16_384 {
2
} else if len < 2_097_152 {
3
} else if len < 268_435_456 {
4
} else {
panic!("Length of remaining bytes must be less than 28 bits")
}
}
pub fn is_valid_topic_name<T: AsRef<str>>(name: T) -> bool {
let name = name.as_ref();
if name.is_empty() || name.len() > 65_535 {
return false;
}
if name.contains('\0') || name.contains('#') || name.contains('+') {
return false;
}
true
}
pub fn is_valid_topic_filter<T: AsRef<str>>(filter: T) -> bool {
let filter = filter.as_ref();
if filter.is_empty() || filter.len() > 65_535 {
return false;
}
if filter.contains('\0') {
return false;
}
if let Some(pos) = filter.find('#') {
if pos != filter.len() - 1 {
return false;
}
if filter.len() > 1 && filter.as_bytes()[pos - 1] != b'/' {
return false;
}
if filter.matches('#').count() > 1 {
return false;
}
}
if filter.contains('+') {
let levels: Vec<&str> = filter.split('/').collect();
for level in levels {
if level.contains('+') && level != "+" {
return false;
}
}
}
true
}
pub fn is_system_topic<T: AsRef<str>>(topic: T) -> bool {
topic.as_ref().starts_with('$')
}
#[cfg(test)]
mod tests {
use crate::protocol::util;
#[test]
fn test_valid_topic_names() {
let topic_names = vec![
"sport/tennis/player1",
"sport/tennis/player1/ranking",
"sport/tennis/player1/score/wimbledon",
"sport",
"sport/",
"/",
"Accounts payable",
"/finance",
"$SYS/monitor/Clients",
];
for name in topic_names {
assert!(util::is_valid_topic_name(name));
}
}
#[test]
fn test_invalid_topic_names() {
let topic_names = vec![
"",
"sport/\0/tennis",
"sport/tennis/player1/#",
"sport+",
"#",
"sport/tennis#",
"sport/tennis/#/ranking",
];
for name in topic_names {
assert!(!util::is_valid_topic_name(name));
}
}
#[test]
fn test_valid_topic_filters() {
let filters = vec![
"sport/tennis/player1/#",
"sport/#",
"#",
"sport/tennis/#",
"+",
"+/tennis/#",
"sport/+/player1",
"/finance",
"$SYS/#",
"$SYS/monitor/+",
];
for filter in filters {
assert!(util::is_valid_topic_filter(filter));
}
}
#[test]
fn test_invalid_topic_filters() {
let filters = vec!["sport/tennis#", "sport/tennis/#/ranking", "sport+", ""];
for filter in filters {
assert!(!util::is_valid_topic_filter(filter));
}
}
#[test]
fn test_topic_filter_multibyte_utf8_before_wildcard() {
assert!(util::is_valid_topic_filter("é/#"));
assert!(util::is_valid_topic_filter("sensors/日本語/temperature"));
assert!(util::is_valid_topic_filter("日本語/#"));
}
#[test]
fn test_topic_filter_multibyte_utf8_without_separator_is_rejected_not_panicking() {
assert!(!util::is_valid_topic_filter("€#"));
}
}