mirror of
https://github.com/Noratrieb/haesli.git
synced 2026-01-14 19:55:03 +01:00
implement topic matching
This commit is contained in:
parent
43d0ce05dc
commit
2abc577aca
1 changed files with 99 additions and 18 deletions
|
|
@ -40,26 +40,62 @@ pub fn route_message(exchange: &Exchange, routing_key: &str) -> Option<Vec<Queue
|
|||
Some(bindings.clone()) // see, this is actually Not That Bad I Hope
|
||||
}
|
||||
ExchangeType::Topic { bindings } => {
|
||||
let topic = parse_topic(routing_key);
|
||||
// todo: optimizing this is a fun problem
|
||||
|
||||
Some(match_topic(bindings, topic))
|
||||
Some(match_topic(bindings, routing_key))
|
||||
}
|
||||
ExchangeType::Headers => None, // unsupported
|
||||
ExchangeType::System => None, // unsupported
|
||||
}
|
||||
}
|
||||
|
||||
fn match_topic<Q: Clone>(
|
||||
patterns: &[(Vec<TopicSegment>, Q)],
|
||||
routing_key: Vec<TopicSegment>,
|
||||
) -> Vec<Q> {
|
||||
fn match_topic<Q: Clone>(patterns: &[(Vec<TopicSegment>, Q)], routing_key: &str) -> Vec<Q> {
|
||||
patterns
|
||||
.iter()
|
||||
.filter_map(|(pattern, value)| {
|
||||
let mut queue_segments = routing_key.iter();
|
||||
let mut key_segments = routing_key.split('.');
|
||||
let mut pat_segments = pattern.iter();
|
||||
|
||||
for segment in pattern {}
|
||||
loop {
|
||||
let key = key_segments.next();
|
||||
let pat = pat_segments.next();
|
||||
|
||||
match (pat, key) {
|
||||
(Some(TopicSegment::Word(pat)), Some(key)) if pat != key => return None,
|
||||
(Some(TopicSegment::Word(_)), Some(_)) => {}
|
||||
(Some(TopicSegment::SingleWildcard), Some(_)) => {}
|
||||
(Some(TopicSegment::MultiWildcard), _) => {
|
||||
let pat = pat_segments.next();
|
||||
match pat {
|
||||
Some(pat) => {
|
||||
// loop until we find the next pat segment in the key
|
||||
loop {
|
||||
let key = key_segments.next();
|
||||
match (pat, key) {
|
||||
(_, None) => return None, // we are expecting something after the wildcard
|
||||
(TopicSegment::Word(pat), Some(key)) if pat == key => {
|
||||
break; // we matched all of the `#` and the segment afterwards
|
||||
}
|
||||
(TopicSegment::SingleWildcard, Some(_)) => {
|
||||
break; // `#.*` is a cursed pattern, match `#` for 1
|
||||
}
|
||||
(TopicSegment::MultiWildcard, Some(_)) => {
|
||||
break; // at this point I don't even care, who the fuck
|
||||
// would use `#.#`, just only match 2 which is
|
||||
// wrong so todo I guess lol
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
None => break, // pattern ends with `#`, it certainly matches
|
||||
}
|
||||
}
|
||||
(Some(_), None) => return None,
|
||||
(None, Some(_)) => return None,
|
||||
(None, None) => break,
|
||||
}
|
||||
}
|
||||
|
||||
Some(value.clone())
|
||||
})
|
||||
|
|
@ -70,18 +106,63 @@ fn match_topic<Q: Clone>(
|
|||
mod tests {
|
||||
use crate::routing::{match_topic, parse_topic};
|
||||
|
||||
macro_rules! match_topics {
|
||||
(patterns: $($pattern:expr),*) => {};
|
||||
macro_rules! match_topics_test {
|
||||
($name:ident {
|
||||
patterns: $($pattern:expr),*;
|
||||
routing_key: $routing_key:expr;
|
||||
expected: $($expected:expr),*;
|
||||
}) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
fn inc(x: &mut u64) -> u64 { let tmp = *x; *x += 1; tmp }
|
||||
|
||||
let mut n = 0;
|
||||
let n = &mut n;
|
||||
|
||||
// assign each pattern a number
|
||||
let patterns = [$((parse_topic($pattern), inc(n))),*];
|
||||
|
||||
let matched = match_topic(&patterns, $routing_key);
|
||||
let expected = vec![$($expected),*];
|
||||
|
||||
assert_eq!(matched, expected);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn match_empty_topic() {
|
||||
let patterns = [(parse_topic(""), 1), (parse_topic("BAD"), 2)];
|
||||
let routing_key = parse_topic("");
|
||||
match_topics_test!(match_spec_example_1 {
|
||||
patterns: "*.stock.#";
|
||||
routing_key: "usd.stock";
|
||||
expected: 0;
|
||||
});
|
||||
|
||||
let matched = match_topic(&patterns, routing_key);
|
||||
match_topics_test!(match_spec_example_2 {
|
||||
patterns: "*.stock.#";
|
||||
routing_key: "eur.stock.db";
|
||||
expected: 0;
|
||||
});
|
||||
|
||||
assert_eq!(matched, vec![1])
|
||||
}
|
||||
match_topics_test!(match_spec_example_3 {
|
||||
patterns: "*.stock.#";
|
||||
routing_key: "stock.nasdaq";
|
||||
expected: ;
|
||||
});
|
||||
|
||||
match_topics_test!(match_no_wildcards {
|
||||
patterns: "na.stock.usd", "sa.stock.peso", "stock.nasdaq", "usd.stock.na";
|
||||
routing_key: "na.stock.usd";
|
||||
expected: 0;
|
||||
});
|
||||
|
||||
match_topics_test!(match_cursed_wildcards {
|
||||
patterns: "*.*.*", "#.usd", "#.stock.*", "*.#", "#", "na.*";
|
||||
routing_key: "na.stock.usd";
|
||||
expected: 0, 1, 2, 3, 4;
|
||||
});
|
||||
|
||||
match_topics_test!(match_empty_topic {
|
||||
patterns: "", "bad";
|
||||
routing_key: "";
|
||||
expected: 0;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue