Files
nomad/plugins/shared/structs/attribute.go
2018-10-11 23:30:28 -07:00

130 lines
2.9 KiB
Go

package structs
import (
"fmt"
"regexp"
"strconv"
)
// BaseUnit is a unique base unit. All units that share the same base unit
// should be comparable.
type BaseUnit uint16
const (
UnitScalar BaseUnit = iota
UnitByte
UnitByteRate
UnitHertz
UnitWatt
)
// Unit describes a unit and its multiplier over the base unit type
type Unit struct {
// Name is the name of the unit (GiB, MB/s)
Name string
// Base is the base unit for the unit
Base BaseUnit
// Multiplier is the multiplier over the base unit (KiB multiplier is 1024)
Multiplier uint64
// InverseMultiplier specifies that the multiplier is an inverse so:
// Base / Multiplier. For example a mW is a W/1000.
InverseMultiplier bool
}
// Comparable returns if two units are comparable
func (u *Unit) Comparable(o *Unit) bool {
if u == nil || o == nil {
return false
}
return u.Base == o.Base
}
// Attribute is used to describe the value of an attribute, optionally
// specifying units
type Attribute struct {
// Float is the float value for the attribute
Float float64
// Int is the int value for the attribute
Int int64
// String is the string value for the attribute
String string
// Bool is the bool value for the attribute
Bool bool
// Unit is the optional unit for the set int or float value
Unit string
}
// Validate checks if the attribute is valid
func (a *Attribute) Validate() error {
if a.Unit != "" {
if _, ok := UnitIndex[a.Unit]; !ok {
return fmt.Errorf("unrecognized unit %q", a.Unit)
}
}
return nil
}
var (
// numericWithUnits matches only if it is a integer or float ending with
// units. It has two capture groups, one for the numeric value and one for
// the unit value
numericWithUnits = regexp.MustCompile(`^([-]?(?:[0-9]+|[0-9]+\.[0-9]+|\.[0-9]+))\s*([a-zA-Z]+\/?[a-zA-z]+|[a-zA-Z])$`)
)
func ParseAttribute(input string) *Attribute {
// Try to parse as a bool
b, err := strconv.ParseBool(input)
if err == nil {
return &Attribute{Bool: b}
}
// Try to parse as a number.
// Check if the string is a number ending with potential units
if matches := numericWithUnits.FindStringSubmatch(input); len(matches) == 3 {
numeric := matches[1]
unit := matches[2]
// Check if we know about the unit. If we don't we can only treat this
// as a string
if _, ok := UnitIndex[unit]; !ok {
return &Attribute{String: input}
}
// Try to parse as an int
i, err := strconv.ParseInt(numeric, 10, 64)
if err == nil {
return &Attribute{Int: i, Unit: unit}
}
// Try to parse as a float
f, err := strconv.ParseFloat(numeric, 64)
if err == nil {
return &Attribute{Float: f, Unit: unit}
}
}
// Try to parse as an int
i, err := strconv.ParseInt(input, 10, 64)
if err == nil {
return &Attribute{Int: i}
}
// Try to parse as a float
f, err := strconv.ParseFloat(input, 64)
if err == nil {
return &Attribute{Float: f}
}
return &Attribute{String: input}
}