pub(crate) fn has_valid_segments(s: &str) -> bool {
!s.is_empty() && s.split('.').all(|seg| !seg.is_empty())
}
pub(crate) fn topic_matches(topic: &str, pattern: &str) -> bool {
if !has_valid_segments(topic) || !has_valid_segments(pattern) {
return false;
}
if topic.split('.').count() != pattern.split('.').count() {
return false;
}
topic
.split('.')
.zip(pattern.split('.'))
.all(|(t, p)| p == "*" || t == p)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn exact_match() {
assert!(topic_matches("user.prompt", "user.prompt"));
assert!(topic_matches(
"llm.stream.anthropic",
"llm.stream.anthropic"
));
}
#[test]
fn wildcard_single_segment() {
assert!(topic_matches(
"tool.execute.search.result",
"tool.execute.*.result"
));
assert!(topic_matches(
"tool.execute.code-run.result",
"tool.execute.*.result"
));
}
#[test]
fn wildcard_does_not_match_missing_segment() {
assert!(!topic_matches(
"tool.execute.result",
"tool.execute.*.result"
));
}
#[test]
fn no_match_different_topic() {
assert!(!topic_matches("user.prompt", "llm.stream.anthropic"));
}
#[test]
fn no_match_extra_segment() {
assert!(!topic_matches("user.prompt.extra", "user.prompt"));
}
#[test]
fn no_match_fewer_segments() {
assert!(!topic_matches("user", "user.prompt"));
}
#[test]
fn single_segment_exact() {
assert!(topic_matches("ping", "ping"));
assert!(!topic_matches("ping", "pong"));
}
#[test]
fn wildcard_at_start() {
assert!(topic_matches("foo.bar.baz", "*.bar.baz"));
}
#[test]
fn wildcard_at_end() {
assert!(topic_matches("foo.bar.baz", "foo.bar.*"));
}
#[test]
fn multiple_wildcards() {
assert!(topic_matches("a.b.c", "*.b.*"));
assert!(topic_matches("x.b.z", "*.b.*"));
assert!(!topic_matches("x.c.z", "*.b.*"));
}
#[test]
fn empty_segments_rejected() {
assert!(!topic_matches("a..b", "a.*.b"));
assert!(!topic_matches("a.x.b", "a..b"));
assert!(!topic_matches(".a.b", "*.a.b"));
assert!(!topic_matches("x.a.b", ".a.b"));
assert!(!topic_matches("a.b.", "a.b.*"));
assert!(!topic_matches("a.b.x", "a.b."));
assert!(!topic_matches(".", "*.*"));
assert!(!topic_matches("", ""));
assert!(!topic_matches("", "a"));
assert!(!topic_matches("a", ""));
}
#[test]
fn has_valid_segments_accepts_valid() {
assert!(has_valid_segments("a"));
assert!(has_valid_segments("a.b"));
assert!(has_valid_segments("a.b.c"));
assert!(has_valid_segments("*"));
assert!(has_valid_segments("a.*.b"));
}
#[test]
fn has_valid_segments_rejects_invalid() {
assert!(!has_valid_segments(""));
assert!(!has_valid_segments("."));
assert!(!has_valid_segments(".."));
assert!(!has_valid_segments("a..b"));
assert!(!has_valid_segments(".a"));
assert!(!has_valid_segments("a."));
assert!(!has_valid_segments(".a.b"));
assert!(!has_valid_segments("a.b."));
assert!(!has_valid_segments("a...b"));
}
}