133 lines
3.4 KiB
Go
133 lines
3.4 KiB
Go
package binary
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type converterFunc func(value interface{}, order binary.ByteOrder) ([]byte, error)
|
|
|
|
type Entry struct {
|
|
ReadFrom string `toml:"read_from"` // field, tag, time, name
|
|
Name string `toml:"name"` // name of entry
|
|
DataFormat string `toml:"data_format"` // int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, string
|
|
StringTerminator string `toml:"string_terminator"` // for string metrics: null, 0x00, 00, ....
|
|
StringLength uint64 `toml:"string_length"` // for string only, target size
|
|
TimeFormat string `toml:"time_format"` // for time metrics: unix, unix_ms, unix_us, unix_ns
|
|
|
|
converter converterFunc
|
|
termination byte
|
|
}
|
|
|
|
func (e *Entry) fillDefaults() error {
|
|
// Normalize
|
|
e.ReadFrom = strings.ToLower(e.ReadFrom)
|
|
|
|
// Check input constraints
|
|
switch e.ReadFrom {
|
|
case "":
|
|
e.ReadFrom = "field"
|
|
fallthrough
|
|
case "field", "tag":
|
|
if e.Name == "" {
|
|
return errors.New("missing name")
|
|
}
|
|
case "time":
|
|
switch e.TimeFormat {
|
|
case "":
|
|
e.TimeFormat = "unix"
|
|
case "unix", "unix_ms", "unix_us", "unix_ns":
|
|
default:
|
|
return errors.New("invalid time format")
|
|
}
|
|
case "name":
|
|
if e.DataFormat == "" {
|
|
e.DataFormat = "string"
|
|
} else if e.DataFormat != "string" {
|
|
return errors.New("name data format has to be string")
|
|
}
|
|
default:
|
|
return fmt.Errorf("unknown assignment %q", e.ReadFrom)
|
|
}
|
|
|
|
// Check data format
|
|
switch e.DataFormat {
|
|
case "":
|
|
return errors.New("missing data format")
|
|
case "float64":
|
|
e.converter = convertToFloat64
|
|
case "float32":
|
|
e.converter = convertToFloat32
|
|
case "uint64":
|
|
e.converter = convertToUint64
|
|
case "uint32":
|
|
e.converter = convertToUint32
|
|
case "uint16":
|
|
e.converter = convertToUint16
|
|
case "uint8":
|
|
e.converter = convertToUint8
|
|
case "int64":
|
|
e.converter = convertToInt64
|
|
case "int32":
|
|
e.converter = convertToInt32
|
|
case "int16":
|
|
e.converter = convertToInt16
|
|
case "int8":
|
|
e.converter = convertToInt8
|
|
case "string":
|
|
switch e.StringTerminator {
|
|
case "", "null":
|
|
e.termination = 0x00
|
|
default:
|
|
e.StringTerminator = strings.TrimPrefix(e.StringTerminator, "0x")
|
|
termination, err := hex.DecodeString(e.StringTerminator)
|
|
if err != nil {
|
|
return fmt.Errorf("decoding terminator failed for %q: %w", e.Name, err)
|
|
}
|
|
if len(termination) != 1 {
|
|
return fmt.Errorf("terminator must be a single byte, got %q", e.StringTerminator)
|
|
}
|
|
e.termination = termination[0]
|
|
}
|
|
|
|
if e.StringLength < 1 {
|
|
return errors.New("string length must be at least 1")
|
|
}
|
|
e.converter = e.convertToString
|
|
default:
|
|
return fmt.Errorf("invalid data format %q for field %q", e.ReadFrom, e.DataFormat)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (e *Entry) serializeValue(value interface{}, order binary.ByteOrder) ([]byte, error) {
|
|
// Handle normal fields, tags, etc
|
|
if e.ReadFrom != "time" {
|
|
return e.converter(value, order)
|
|
}
|
|
|
|
// We need to serialize the time, make sure we actually do get a time and
|
|
// convert it to the correct timestamp (with scale) first.
|
|
t, ok := value.(time.Time)
|
|
if !ok {
|
|
return nil, fmt.Errorf("time expected but got %T", value)
|
|
}
|
|
|
|
var timestamp int64
|
|
switch e.TimeFormat {
|
|
case "unix":
|
|
timestamp = t.Unix()
|
|
case "unix_ms":
|
|
timestamp = t.UnixMilli()
|
|
case "unix_us":
|
|
timestamp = t.UnixMicro()
|
|
case "unix_ns":
|
|
timestamp = t.UnixNano()
|
|
}
|
|
return e.converter(timestamp, order)
|
|
}
|