package driver import ( "bytes" "encoding/json" "fmt" "time" "onehouse/protocol" ) type publicationValue struct { publication protocol.Publication address protocol.Address metadata protocol.Metadata payload any comparison []byte measurement bool online bool } func newPublicationValue[T any](address protocol.Address, metadata protocol.Metadata, payload T) (publicationValue, error) { metadata, err := newMetadata(metadata) if err != nil { return publicationValue{}, fmt.Errorf("create publication metadata: %w", err) } value := publicationValue{ address: address, metadata: metadata, payload: payload, } value.publication, err = value.build() if err != nil { return publicationValue{}, fmt.Errorf("build typed publication: %w", err) } data, err := json.Marshal(payload) if err != nil { return publicationValue{}, fmt.Errorf("encode comparison payload: %w", err) } var normalized any decoder := json.NewDecoder(bytes.NewReader(data)) decoder.UseNumber() err = decoder.Decode(&normalized) if err != nil { return publicationValue{}, fmt.Errorf("decode comparison payload: %w", err) } comparison, err := json.Marshal(normalized) if err != nil { return publicationValue{}, fmt.Errorf("normalize comparison payload: %w", err) } var copied T err = json.Unmarshal(comparison, &copied) if err != nil { return publicationValue{}, fmt.Errorf("copy publication payload: %w", err) } value.payload = copied value.comparison = comparison switch typed := any(copied).(type) { case protocol.ValuePayload: value.measurement = address.Kind == protocol.Telemetry || string(typed.Value) != "true" && string(typed.Value) != "false" case protocol.AvailabilityPayload: value.online = typed.Status == "online" } return value, nil } func newMetadata(metadata protocol.Metadata) (protocol.Metadata, error) { now := time.Now().UTC() if metadata.MessageID == "" { id, err := protocol.NewID(now) if err != nil { return protocol.Metadata{}, fmt.Errorf("create message ID: %w", err) } metadata.MessageID = id } if metadata.SentAt == nil { metadata.SentAt = &now } else { timestamp := *metadata.SentAt metadata.SentAt = ×tamp } if metadata.ExpiresAt != nil { timestamp := *metadata.ExpiresAt metadata.ExpiresAt = ×tamp } return metadata, nil } func (v publicationValue) build() (protocol.Publication, error) { var ( publication protocol.Publication err error ) switch payload := v.payload.(type) { case protocol.DescriptorPayload: publication, err = protocol.BuildDescriptor(v.address.ID, v.metadata, payload) case protocol.AvailabilityPayload: publication, err = protocol.BuildAvailability(v.address.ID, v.metadata, payload) case protocol.ValuePayload: if v.address.Kind == protocol.Telemetry { publication, err = protocol.BuildTelemetry(v.address.ID, v.address.Target, v.metadata, payload) } else { publication, err = protocol.BuildReported(v.address.ID, v.address.Target, v.metadata, payload) } case protocol.EventPayload: publication, err = protocol.BuildEvent(v.address.ID, v.address.Target, v.metadata, payload) case protocol.ResultPayload: publication, err = protocol.BuildResult(v.address.ID, v.address.Target, v.metadata, payload) default: return protocol.Publication{}, fmt.Errorf("unsupported publication payload") } if err != nil { return protocol.Publication{}, fmt.Errorf("build %s: %w", v.address.Kind, err) } return publication, nil } func (v publicationValue) repeated() (publicationValue, error) { metadata, err := newMetadata(protocol.Metadata{SentAt: v.metadata.SentAt}) if err != nil { return publicationValue{}, fmt.Errorf("create repeat metadata: %w", err) } v.metadata = metadata if v.address.Kind == protocol.Telemetry { v.address.Kind = protocol.Reported } v.publication, err = v.build() if err != nil { return publicationValue{}, fmt.Errorf("build repeated publication: %w", err) } return v, nil }