Skip to content

Commit a08a941

Browse files
committed
Fixed TopicMatcher and TopicFilter. Added topic_matcher() functions from PR #228
1 parent cf953b9 commit a08a941

File tree

4 files changed

+424
-215
lines changed

4 files changed

+424
-215
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
55

66
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [v0.12.4](https://github.com/eclipse/paho.mqtt.rust/compare/v0.12.3..v0.12.4) - 2024-05-19
9+
10+
- Fixes for topic matching:
11+
- `TopicMatcher`
12+
- Fixed a number of corner cases
13+
- Iterator optimized
14+
- Added `prune()` and `shrink_to_fit()`, and `get_key_value()`
15+
- `TopicFilter` fixed corner cases
16+
- Added stand-alone `topic_matches()` and `topic_matches_iter()` functions from [PR #228](https://github.com/eclipse/paho.mqtt.rust/pull/228)
17+
18+
819
## [v0.12.3](https://github.com/eclipse/paho.mqtt.rust/compare/v0.12.2..v0.12.3) - 2023-10-25
920

1021
- The -sys crate now wraps Paho C v1.3.13, fixing several issues, including crashes on reconnect callbacks.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "paho-mqtt"
3-
version = "0.12.3"
3+
version = "0.12.4"
44
edition = "2021"
55
rust-version = "1.63"
66
authors = ["Frank Pagliughi <fpagliughi@mindspring.com>"]

src/topic.rs

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -457,40 +457,45 @@ impl TopicFilter {
457457
Ok(v)
458458
}
459459

460+
/// Creates a new topic filter from the string without checking it.
461+
pub fn new_unchecked<S>(filter: S) -> Self
462+
where
463+
S: Into<String>,
464+
{
465+
let filter = filter.into();
466+
467+
if filter.contains('+') || filter.ends_with('#') {
468+
Self::Fields(filter.split('/').map(|s| s.to_string()).collect())
469+
}
470+
else {
471+
Self::Topic(filter)
472+
}
473+
}
474+
460475
/// Determines if the topic matches the filter.
461-
pub fn is_match(&self, topic: &str) -> bool {
476+
///
477+
/// This is the same as [`is_match`](Self::is_match), but uses a more
478+
/// consistent function name with other topic matchers.
479+
pub fn matches(&self, topic: &str) -> bool {
480+
use crate::topic_matcher::topic_matches_iter;
462481
match self {
463482
Self::Topic(filter) => topic == filter,
464483
Self::Fields(fields) => {
465-
let n = fields.len();
466-
let top_fields: Vec<_> = topic.split('/').collect();
467-
468-
if n > top_fields.len() {
469-
false
470-
}
471-
else {
472-
let mut saw_wc = false;
473-
for i in 0..n {
474-
if fields[i] == "#" {
475-
saw_wc = true;
476-
break;
477-
}
478-
if fields[i] != "+" && fields[i] != top_fields[i] {
479-
return false;
480-
}
481-
}
482-
saw_wc || n == top_fields.len()
483-
}
484+
topic_matches_iter(fields.iter().map(|s| s.as_str()), topic.split('/'))
484485
}
485486
}
486487
}
488+
489+
/// Determines if the topic matches the filter.
490+
pub fn is_match(&self, topic: &str) -> bool {
491+
self.matches(topic)
492+
}
487493
}
488494

489495
impl fmt::Display for TopicFilter {
490496
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
491497
match self {
492498
Self::Topic(filter) => write!(f, "{}", filter),
493-
// OPTIIMIZE: Do the individual writes, not join
494499
Self::Fields(fields) => write!(f, "{}", fields.join("/")),
495500
}
496501
}
@@ -516,7 +521,7 @@ mod tests {
516521
}
517522

518523
#[test]
519-
fn test_topic_filter() {
524+
fn test_basic_topic_filter() {
520525
const FILTER1: &str = "some/topic/#";
521526

522527
let filter = TopicFilter::new(FILTER1).unwrap();
@@ -538,4 +543,33 @@ mod tests {
538543
assert!(filter.is_match("some/thing"));
539544
assert!(!filter.is_match("some/thing/plus"));
540545
}
546+
547+
#[test]
548+
fn test_topic_filter() {
549+
// Should match
550+
551+
assert!(TopicFilter::new_unchecked("foo/bar").matches("foo/bar"));
552+
assert!(TopicFilter::new_unchecked("foo/+").matches("foo/bar"));
553+
assert!(TopicFilter::new_unchecked("foo/+/baz").matches("foo/bar/baz"));
554+
assert!(TopicFilter::new_unchecked("foo/+/#").matches("foo/bar/baz"));
555+
assert!(TopicFilter::new_unchecked("A/B/+/#").matches("A/B/B/C"));
556+
assert!(TopicFilter::new_unchecked("#").matches("foo/bar/baz"));
557+
assert!(TopicFilter::new_unchecked("#").matches("/foo/bar"));
558+
assert!(TopicFilter::new_unchecked("/#").matches("/foo/bar"));
559+
assert!(TopicFilter::new_unchecked("$SYS/bar").matches("$SYS/bar"));
560+
assert!(TopicFilter::new_unchecked("foo/#").matches("foo/$bar"));
561+
assert!(TopicFilter::new_unchecked("foo/+/baz").matches("foo/$bar/baz"));
562+
563+
// Should not match
564+
565+
assert!(!TopicFilter::new_unchecked("test/6/#").matches("test/3"));
566+
assert!(!TopicFilter::new_unchecked("foo/bar").matches("foo"));
567+
assert!(!TopicFilter::new_unchecked("foo/+").matches("foo/bar/baz"));
568+
assert!(!TopicFilter::new_unchecked("foo/+/baz").matches("foo/bar/bar"));
569+
assert!(!TopicFilter::new_unchecked("foo/+/#").matches("fo2/bar/baz"));
570+
assert!(!TopicFilter::new_unchecked("/#").matches("foo/bar"));
571+
assert!(!TopicFilter::new_unchecked("#").matches("$SYS/bar"));
572+
assert!(!TopicFilter::new_unchecked("$BOB/bar").matches("$SYS/bar"));
573+
assert!(!TopicFilter::new_unchecked("+/bar").matches("$SYS/bar"));
574+
}
541575
}

0 commit comments

Comments
 (0)