package versioning import ( "fmt" "strconv" "strings" "github.com/pkg/errors" ) const ( MaxMajor uint16 = 42948 MaxMinor uint16 = 999 MaxPatch uint8 = 99 ) var maxVersion = newVersion(MaxMajor, MaxMinor, MaxPatch) type Version struct { major uint16 minor uint16 patch uint8 code uint32 } func (v Version) Major() uint16 { return v.major } func (v Version) Minor() uint16 { return v.minor } func (v Version) Patch() uint8 { return v.patch } func (v Version) Code() uint32 { return v.code } func (a Version) Equal(b Version) bool { return a.Code() == b.Code() } func (a Version) Less(b Version) bool { return a.Code() < b.Code() } func (a Version) Lte(b Version) bool { return a.Code() <= b.Code() } func (a Version) Greater(b Version) bool { return a.Code() > b.Code() } func (a Version) Gte(b Version) bool { return a.Code() >= b.Code() } func (v Version) String() string { return fmt.Sprintf("%d.%d.%d", v.major, v.minor, v.patch) } func ParseVersion(str string) (Version, error) { parts := strings.Split(str, ".") partsLen := len(parts) if partsLen > 3 { return Version{}, errors.Errorf("invalid game version: '%s'", str) } maj, err := parseUint16(parts[0]) if err != nil { return Version{}, err } var ( min uint16 patch uint8 ) if partsLen > 1 { min, err = parseUint16(parts[1]) if err != nil { return Version{}, err } } if partsLen > 2 { patch, err = parseUint8(parts[2]) if err != nil { return Version{}, err } } if err := validate(maj, min, patch); err != nil { return Version{}, err } return newVersion(maj, min, patch), nil } func MustParseVersion(str string) Version { v, err := ParseVersion(str) if err != nil { return Version{} } return v } func ParseVersionFromCode(code uint32) Version { patch := code % 100 minor := code%100000 - patch major := code - minor - patch maj, min, p := uint16(major/100000), uint16(minor/100), uint8(patch) return Version{ major: maj, minor: min, patch: p, code: code, } } func parseUint16(str string) (uint16, error) { v, err := strconv.ParseUint(str, 10, 16) if err != nil { return 0, errors.WithStack(err) } return uint16(v), nil } func parseUint8(str string) (uint8, error) { v, err := strconv.ParseUint(str, 10, 8) if err != nil { return 0, errors.WithStack(err) } return uint8(v), nil } func validate(major, minor uint16, patch uint8) error { if major > MaxMajor || minor > MaxMinor || patch > MaxPatch { return errors.Errorf("invalid game version. max version: %s", maxVersion) } return nil } func newVersion(major, minor uint16, patch uint8) Version { return Version{ major: major, minor: minor, patch: patch, code: calcCode(major, minor, patch), } } func calcCode(major, minor uint16, patch uint8) uint32 { return uint32(major)*100000 + uint32(minor)*100 + uint32(patch) }