X-Git-Url: https://git.giacomolli.uk/onehouse/blobdiff_plain/c376881c67694cd9931d80fb4a04ec69751d222b..HEAD:/protocol/message.go diff --git a/protocol/message.go b/protocol/message.go index cb575d2..50a2e1c 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -42,41 +42,28 @@ func Parse(topic string, data []byte, retained bool) (Message[any], error) { switch address.Kind { case Descriptor: - return eraseMessage(ParseDescriptor(topic, data, retained)) + return eraseMessage(parsePayload[DescriptorPayload](address, data, retained)) case Availability: - return eraseMessage(ParseAvailability(topic, data, retained)) + return eraseMessage(parsePayload[AvailabilityPayload](address, data, retained)) case Reported: - return eraseMessage(ParseReported(topic, data, retained)) + return eraseMessage(parsePayload[ValuePayload](address, data, retained)) case Telemetry: - return eraseMessage(ParseTelemetry(topic, data, retained)) + return eraseMessage(parsePayload[ValuePayload](address, data, retained)) case Event: - return eraseMessage(ParseEvent(topic, data, retained)) + return eraseMessage(parsePayload[EventPayload](address, data, retained)) case Command: - return eraseMessage(ParseCommand(topic, data, retained)) + return eraseMessage(parsePayload[ValuePayload](address, data, retained)) case CommandBatch: - return eraseMessage(ParseCommandBatch(topic, data, retained)) + return eraseMessage(parsePayload[BatchPayload](address, data, retained)) case Result: - return eraseMessage(ParseResult(topic, data, retained)) + return eraseMessage(parsePayload[ResultPayload](address, data, retained)) case HubStatus: - return eraseMessage(ParseHubStatus(topic, data, retained)) + return eraseMessage(parsePayload[HubStatusPayload](address, data, retained)) default: return Message[any]{}, fmt.Errorf("unsupported message kind") } } -func eraseMessage[T any](message Message[T], err error) (Message[any], error) { - if err != nil { - return Message[any]{}, fmt.Errorf("parse message: %w", err) - } - - return Message[any]{ - Address: message.Address, - Metadata: message.Metadata, - Payload: message.Payload, - Retained: message.Retained, - }, nil -} - func buildMessage[T any](address Address, metadata Metadata, payload T) (Publication, error) { topic, err := address.topic() if err != nil { @@ -156,6 +143,19 @@ func parseMessage[T any](kind Kind, topic string, data []byte, retained bool) (M return message, nil } +func eraseMessage[T any](message Message[T], err error) (Message[any], error) { + if err != nil { + return Message[any]{}, fmt.Errorf("parse message: %w", err) + } + + return Message[any]{ + Address: message.Address, + Metadata: message.Metadata, + Payload: message.Payload, + Retained: message.Retained, + }, nil +} + func parsePayload[T any](address Address, data []byte, retained bool) (Message[T], error) { kind := address.Kind @@ -252,28 +252,6 @@ func validatePayload(address Address, metadata Metadata, payload any) error { return nil } -func validateDescriptor(payload DescriptorPayload) error { - if payload.Manufacturer == "" || payload.Model == "" || payload.Firmware == "" || len(string([]rune(payload.Attribution))) > 2000 || len(payload.Capabilities) == 0 { - return fmt.Errorf("descriptor metadata or capabilities are invalid") - } - - seen := make(map[string]bool, len(payload.Capabilities)) - - for _, capability := range payload.Capabilities { - if !ValidIdentifier(capability.ID) || !ValidIdentifier(capability.Type) || seen[capability.ID] { - return fmt.Errorf("capability IDs and types must be valid and unique") - } - - if !capability.ReportsState && !capability.AcceptsCommands { - return fmt.Errorf("capability must have at least one access mode") - } - - seen[capability.ID] = true - } - - return nil -} - func validateDescriptorFlags(raw json.RawMessage) error { var fields struct { Capabilities []map[string]json.RawMessage `json:"capabilities"` @@ -296,6 +274,28 @@ func validateDescriptorFlags(raw json.RawMessage) error { return nil } +func validateDescriptor(payload DescriptorPayload) error { + if payload.Manufacturer == "" || payload.Model == "" || payload.Firmware == "" || len(string([]rune(payload.Attribution))) > 2000 || len(payload.Capabilities) == 0 { + return fmt.Errorf("descriptor metadata or capabilities are invalid") + } + + seen := make(map[string]bool, len(payload.Capabilities)) + + for _, capability := range payload.Capabilities { + if !ValidIdentifier(capability.ID) || !ValidIdentifier(capability.Type) || seen[capability.ID] { + return fmt.Errorf("capability IDs and types must be valid and unique") + } + + if !capability.ReportsState && !capability.AcceptsCommands { + return fmt.Errorf("capability must have at least one access mode") + } + + seen[capability.ID] = true + } + + return nil +} + func validateAvailability(payload AvailabilityPayload) error { if payload.Status != "online" && payload.Status != "offline" { return fmt.Errorf("availability must be online or offline")