package apiversion import ( "fmt" "regexp" "strconv" "github.com/pkg/errors" ) // API version names follow k8s style version names: // valid API version names examples: "v1", "v1alpha2", "v2beta3", etc... var nameRegex = regexp.MustCompile("^v([1-9][0-9]*)(?:(alpha|beta)([1-9][0-9]*))?$") type qualifier uint const ( alpha qualifier = iota beta stable ) type Version struct { // major version number, eg 1 for "v1", 2 for "v2beta3" major uint // qualifier is "alpha" or "beta" qualifier qualifier // the qualifier's version, eg 2 for "v1alpha2" or 3 for "v2beta3" qualifierVersion uint rawName string } // NewVersion builds a Version from a version string, eg "v1alpha2" func NewVersion(name string) (version Version, err error) { matches := nameRegex.FindStringSubmatch(name) if len(matches) < 2 || matches[1] == "" { err = fmt.Errorf("invalid version name: %q", name) return } major, err := toUint(matches[1], name) if err != nil { return } qualifier := stable var qualifierVersion uint if len(matches) >= 4 && matches[2] != "" { switch matches[2] { case "alpha": qualifier = alpha case "beta": qualifier = beta } qualifierVersion, err = toUint(matches[3], name) if err != nil { return } } version.major = major version.qualifier = qualifier version.qualifierVersion = qualifierVersion version.rawName = name return } // NewVersionOrPanic is the same as NewVersion, but panics if // passed an invalid version name. func NewVersionOrPanic(name string) Version { version, err := NewVersion(name) if err != nil { panic(err) } return version } // IsValidVersion returns true iff the input is a valid version name. func IsValidVersion(name string) bool { return nameRegex.MatchString(name) } func toUint(s, name string) (uint, error) { i, err := strconv.ParseUint(s, 10, 0) if err != nil { return 0, errors.Wrapf(err, "unable to convert %q from version name %q to int", s, name) } return uint(i), nil } type Comparison int const ( Lesser Comparison = -1 Equal Comparison = 0 Greater Comparison = 1 ) // Compare return Lesser if v < other (ie other is more recent), Equal if v == other, // and Greater if v > other (ie v is more recent) func (v Version) Compare(other Version) Comparison { if cmp := compareUints(v.major, other.major); cmp != Equal { return cmp } if cmp := compareUints(uint(v.qualifier), uint(other.qualifier)); cmp != Equal { return cmp } return compareUints(v.qualifierVersion, other.qualifierVersion) } func compareUints(a, b uint) Comparison { if a < b { return Lesser } if a > b { return Greater } return Equal } func (v Version) String() string { return v.rawName }