MqttX.Topic (MqttX v0.7.0)

View Source

MQTT Topic validation, normalization, and wildcard matching.

Topic Format

Topics are /-separated strings. Wildcards are:

  • + - Single-level wildcard (matches exactly one level)
  • # - Multi-level wildcard (matches zero or more levels, must be last)

Shared Subscriptions (MQTT 5.0)

Shared subscriptions use the format $share/group_name/topic_filter. Messages are distributed among subscribers in a group using round-robin.

Examples

# Validation
iex> MqttX.Topic.validate("sensors/temperature")
{:ok, ["sensors", "temperature"]}

iex> MqttX.Topic.validate("sensors/+/humidity")
{:ok, ["sensors", :single_level, "humidity"]}

iex> MqttX.Topic.validate("sensors/#")
{:ok, ["sensors", :multi_level]}

# Matching
iex> MqttX.Topic.matches?(["sensors", :single_level, "temp"], ["sensors", "room1", "temp"])
true

iex> MqttX.Topic.matches?(["sensors", :multi_level], ["sensors", "room1", "temp"])
true

# Shared subscriptions
iex> MqttX.Topic.shared?("$share/group1/sensors/#")
true

iex> MqttX.Topic.parse_shared("$share/group1/sensors/#")
{:shared, "group1", "sensors/#"}

Summary

Functions

Flatten a normalized topic back to a binary string.

Match a topic filter against a concrete topic.

Normalize a topic to a list.

Parse a shared subscription filter.

Check if a topic filter is a shared subscription.

Check if a topic is valid.

Validate and normalize a topic.

Validate a topic for publishing (no wildcards allowed).

Check if a topic contains wildcards.

Types

normalized_topic()

@type normalized_topic() :: [binary() | wildcard()]

wildcard()

@type wildcard() :: :single_level | :multi_level

Functions

flatten(topic)

@spec flatten(normalized_topic()) :: binary()

Flatten a normalized topic back to a binary string.

matches?(filter, topic)

@spec matches?(normalized_topic(), normalized_topic()) :: boolean()

Match a topic filter against a concrete topic.

The filter can contain wildcards, the topic should not.

Examples

iex> MqttX.Topic.matches?(["sensors", :single_level, "temp"], ["sensors", "room1", "temp"])
true

iex> MqttX.Topic.matches?(["sensors", :multi_level], ["sensors", "room1", "temp"])
true

iex> MqttX.Topic.matches?(["sensors", "room1"], ["sensors", "room2"])
false

normalize(topic)

@spec normalize(binary() | list()) :: normalized_topic()

Normalize a topic to a list.

Wildcards are converted to atoms :single_level (+) and :multi_level (#).

parse_shared(topic)

@spec parse_shared(binary()) :: {:shared, binary(), binary()} | {:normal, binary()}

Parse a shared subscription filter.

Returns {:shared, group_name, topic_filter} for shared subscriptions, or {:normal, topic_filter} for regular subscriptions.

Examples

iex> MqttX.Topic.parse_shared("$share/group1/sensors/#")
{:shared, "group1", "sensors/#"}

iex> MqttX.Topic.parse_shared("sensors/#")
{:normal, "sensors/#"}

shared?(arg1)

@spec shared?(binary()) :: boolean()

Check if a topic filter is a shared subscription.

Shared subscriptions use the format $share/group_name/topic_filter.

Examples

iex> MqttX.Topic.shared?("$share/group1/sensors/#")
true

iex> MqttX.Topic.shared?("sensors/#")
false

valid?(topic)

@spec valid?(binary() | list()) :: boolean()

Check if a topic is valid.

validate(topic)

@spec validate(binary() | list()) ::
  {:ok, normalized_topic()} | {:error, :invalid_topic}

Validate and normalize a topic.

Returns {:ok, normalized_topic} or {:error, :invalid_topic}.

validate_publish(topic)

@spec validate_publish(binary() | list()) ::
  {:ok, normalized_topic()} | {:error, :invalid_topic}

Validate a topic for publishing (no wildcards allowed).

Returns {:ok, normalized_topic} or {:error, :invalid_topic}.

wildcard?(topic)

@spec wildcard?(normalized_topic()) :: boolean()

Check if a topic contains wildcards.