Created
October 21, 2016 17:09
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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