Attribute Macro mqtt_topic

Source
#[mqtt_topic]
Expand description

Generate a typed MQTT subscriber and/or publisher from a struct and topic pattern

This macro analyzes the annotated struct and generates:

  1. FromMqttMessage trait implementation for message conversion (if subscriber enabled)
  2. Helper constants (TOPIC_PATTERN, MQTT_PATTERN)
  3. Async subscribe() method for easy subscription (if subscriber enabled)
  4. publish() and get_publisher() methods for publishing (if publisher enabled)

§Arguments

The macro takes a topic pattern string and optional mode flags:

  • #[mqtt_topic("pattern")] - Generate both subscriber and publisher (default)
  • #[mqtt_topic("pattern", subscriber)] - Generate only subscriber
  • #[mqtt_topic("pattern", publisher)] - Generate only publisher
  • #[mqtt_topic("pattern", subscriber, publisher)] - Generate both (explicit)

The pattern can include:

  • Literal segments: sensors, data, status
  • Named wildcards: {sensor_id}, {room}, {device_type}
  • Anonymous wildcards: + (single level), # (multi-level, subscriber-only)

§Struct Requirements

The annotated struct must:

  • Be a struct with named fields
  • Only contain fields that correspond to:
    • Topic parameters (matching {param} names in the pattern)
    • payload field (optional, for message data)
    • topic field (optional, must be Arc<TopicMatch>)

§Generated Code

For a struct annotated with #[mqtt_topic("sensors/{id}/data")]:

// Subscriber functionality (if enabled)
impl<DE> FromMqttMessage<PayloadType, DE> for YourStruct {
    fn from_mqtt_message(
        topic: Arc<TopicMatch>,
        payload: PayloadType,
    ) -> Result<Self, MessageConversionError<DE>> {
        // Parameter extraction and struct construction
    }
}

impl YourStruct {
    pub const TOPIC_PATTERN: &'static str = "sensors/{id}/data";
    pub const MQTT_PATTERN: &'static str = "sensors/+/data";
     
    // Subscriber methods (if enabled)
    pub async fn subscribe<F>(client: &MqttClient<F>) -> Result<...> {
        // Subscription logic
    }
     
    // Publisher methods (if enabled)
    pub async fn publish<F>(client: &MqttClient<F>, id: ParamType, data: &PayloadType) -> Result<...> {
        // Publishing logic
    }
     
    pub fn get_publisher<F>(client: &MqttClient<F>, id: ParamType) -> Result<...> {
        // Publisher creation
    }
}

§Examples

§Basic Usage (Both Modes)

#[derive(Debug)]
#[mqtt_topic("sensors/{sensor_id}/temperature")]
struct TemperatureReading {
    sensor_id: u32,
    payload: f64,
}

§Subscriber Only

#[derive(Debug)]
#[mqtt_topic("devices/{device_id}/status/#", subscriber)]
struct DeviceStatus {
    device_id: String,
    payload: Vec<u8>,
    topic: Arc<TopicMatch>,  // Access to full topic match
}

§Publisher Only

#[derive(Debug)]
#[mqtt_topic("commands/{service}/{action}", publisher)]
struct Command {
    service: String,
    action: String,
    payload: String,
}

§Multiple Parameters

#[derive(Debug)]
#[mqtt_topic("buildings/{building}/floors/{floor}/rooms/{room}/sensors/{sensor_id}")]
struct SensorReading {
    building: String,
    floor: u32,
    room: String,
    sensor_id: u32,
    payload: Vec<u8>,
}

§No Payload

#[derive(Debug)]
#[mqtt_topic("heartbeat/{service_name}")]
struct Heartbeat {
    service_name: String,
    // No payload field - will default to Vec<u8>
}

§Error Handling

The macro performs compile-time validation and will produce helpful error messages for common issues:

  • Unknown fields that don’t match topic parameters
  • Invalid topic patterns (e.g., # not at the end)
  • Incorrect type for topic field
  • Non-struct types or structs without named fields
  • Publisher mode with # wildcards (not supported)

§Runtime Behavior

§Subscriber

When messages are received:

  1. Topic is matched against the pattern
  2. Named parameters are extracted and parsed to their field types
  3. Payload is deserialized to the payload field type
  4. Struct is constructed with all extracted values

§Publisher

When publishing messages:

  1. Topic parameters are provided as method arguments
  2. Topic string is constructed from the pattern
  3. Payload is serialized and published

If parameter parsing fails (e.g., non-numeric string for u32 field), a MessageConversionError is returned.