// Package mq exposes the broker-agnostic messaging seam used by services. package mq import ( "context" "encoding/json" "errors" "time" ) var ErrUnavailable = errors.New("message broker unavailable") const ( ProviderEMQX = "emqx" ProviderKafka = "kafka" ProviderRabbitMQ = "rabbitmq" ) const ( AtMostOnce byte = 0 AtLeastOnce byte = 1 ExactlyOnce byte = 2 ) type Message struct { Topic string Payload []byte QoS byte Retain bool } func (m Message) DecodeJSON(target any) error { return json.Unmarshal(m.Payload, target) } type Handler func(context.Context, Message) // TopicSubscription is one logical module subscription. The handler is kept // by the runtime and is replayed after a client reconnects or is rebuilt. type TopicSubscription struct { Topic string QoS byte Handler Handler } // SubscriptionSet is the complete subscription declaration for one module and // provider. Registering the same owner/provider replaces its previous set. // Owner must be a stable module name, not a request or goroutine identifier. type SubscriptionSet struct { Owner string Provider string Topics []TopicSubscription } // SubscriptionRegistrar is the module-facing seam for durable-in-process // subscription intent. Register does not require a live broker; the runtime // will bind the declared topics when the provider becomes available. type SubscriptionRegistrar interface { Register(SubscriptionSet) error Unregister(owner string) error } // SubscriptionContributor lets a module expose its broker subscriptions // without depending on the concrete integration implementation. type SubscriptionContributor interface { RegisterSubscriptions(SubscriptionRegistrar) error } type Client interface { Publish(context.Context, string, []byte, byte, bool) error Subscribe(context.Context, string, byte, Handler) error Unsubscribe(context.Context, ...string) error Connected() bool Close() error } // Registry exposes named broker clients while preserving Client as the // default EMQX/MQTT boundary for existing modules. type Registry interface { SubscriptionRegistrar Client(provider string) Client PublishTo(context.Context, string, string, []byte, byte, bool) error SubscribeTo(context.Context, string, string, byte, Handler) error UnsubscribeFrom(context.Context, string, ...string) error ConnectedTo(provider string) bool } type Config struct { Enabled bool Broker string ClientID string Username string Password string KeepAlive time.Duration CleanSession bool ConnectTimeout time.Duration ReconnectInterval time.Duration } func PublishJSON(ctx context.Context, client Client, topic string, value any, qos byte, retain bool) error { if client == nil { return ErrUnavailable } payload, err := json.Marshal(value) if err != nil { return err } return client.Publish(ctx, topic, payload, qos, retain) }