Skip to content

Instantly share code, notes, and snippets.

@inhies
Created October 21, 2016 17:09
Show Gist options
  • Save inhies/2c80924727fa8eba0d5465da6dbef33e to your computer and use it in GitHub Desktop.
Save inhies/2c80924727fa8eba0d5465da6dbef33e to your computer and use it in GitHub Desktop.
Checks an MQTT topic against an MQTT subscription to see if it matches. Supports MQTT wildcards.
package main
import (
"log"
"strings"
)
type check struct {
Sub, Topic string
ShouldMatch bool
}
func main() {
tests := []check{
{"myhome/groundfloor/+/temperature", "myhome/groundfloor/livingroom/temperature", true},
{"myhome/groundfloor/+/temperature", "myhome/groundfloor/kitchen/temperature", true},
{"myhome/groundfloor/+/temperature", "myhome/groundfloor/kitchen/brightness", false},
{"myhome/groundfloor/+/temperature", "myhome/firstfloor/kitchen/temperature", false},
{"myhome/groundfloor/+/temperature", "myhome/groundfloor/kitchen/fridge/temperature", false},
{"myhome/groundfloor/#", "myhome/groundfloor/livingroom/temperature", true},
{"myhome/groundfloor/#", "myhome/groundfloor/kitchen/temperature", true},
{"myhome/groundfloor/#", "myhome/groundfloor/kitchen/brightness", true},
{"myhome/groundfloor/#", "myhome/firstfloor/kitchen/temperature", false},
{"a/b/c/d", "a/b/c/d", true},
{"+/b/c/d", "a/b/c/d", true},
{"a/+/c/d", "a/b/c/d", true},
{"a/+/+/d", "a/b/c/d", true},
{"+/+/+/+", "a/b/c/d", true},
{"a/b/c", "a/b/c/d", false},
{"b/+/c/d", "a/b/c/d", false},
{"+/+/+", "a/b/c/d", false},
{"#", "a/b/c/d", true},
{"a/#", "a/b/c/d", true},
{"a/b/#", "a/b/c/d", true},
{"a/b/c/#", "a/b/c/d", true},
{"+/b/c/#", "a/b/c/d", true},
{"a/+/topic", "a//topic", true},
{"+/a/topic", "/a/topic", true},
{"#", "/a/topic", true},
{"a/topic/+", "a/topic/", true},
{"a/topic/#", "a/topic/", true},
{"sport/tennis/player1/#", "sport/tennis/player1", true},
{"sport/tennis/player1/#", "sport/tennis/player1/ranking", true},
{"sport/tennis/player1/#", "sport/tennis/player1/score/wimbledon", true},
{"sport/#", "sport/tennis/player1", true},
{"sport/tennis/#", "sport/tennis/player1", true},
{"sport/tennis#", "sport/tennis/player1", false},
{"sport/tennis/#/ranking", "sport/tennis/player1", false},
{"sport/tennis/+", "sport/tennis/player1", true},
{"sport/tennis/+", "sport/tennis/player1/ranking", false},
{"sport/+", "sport", false},
{"sport/+", "sport/", true},
{"+/+", "/finance", true},
{"/+", "/finance", true},
{"+", "/finance", false},
}
for i, v := range tests {
if CheckTopic(v.Sub, v.Topic) != v.ShouldMatch {
log.Fatalf("Failed on %v: %v", i, v)
}
}
}
// CheckTopic returns true if a message topic satisfies
// a message subscription.
func CheckTopic(sub, topic string) bool {
s := strings.Split(sub, "/")
t := strings.Split(topic, "/")
// Check for leading '$' first, becuase we don't want to accidentally
// match it using a wildcard later. '$' topics are reserved for the
// broker to use and is not for clients.
if (sub[0] == '$' && topic[0] != '$') ||
(topic[0] == '$' && sub[0] != '$') {
return false
}
// Work through the subscription section by section and see if it matches the
// topic exatly or via wildcards.
for i := 0; i < len(s) && i < len(t); i++ {
switch s[i] {
case "+":
//Single wildcard, dont need to compare
continue
case "#":
if i < len(s)-1 {
// Invalid, # is not the last character
return false
}
// Everything after this doesn't matter
return true
default:
if s[i] != t[i] {
// This section of the topic and subscription
// dont match, so the topic is not a match for
// the subscription
return false
}
}
}
// If we made it this far, everything has matched so far
if len(s) == len(t) {
// Topic and subscription both have the same amount of sections
// and they all matched so return true.
return true
} else {
// the topic and subscription have a mismatch in the number of
// sections, but if the last character is this wildcard, then
// everything will match
if s[len(s)-1] == "#" {
return true
}
// Either the topic or subscription is longer than the other
// and the wildcard is not on the end, so they are not a match
return false
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment