183 lines
4.3 KiB
Go
183 lines
4.3 KiB
Go
package version
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
type VerSegComp interface {
|
|
Validate() error
|
|
// Compare should panic if incompatible interface is given
|
|
Compare(VerSegComp) int
|
|
AsString() string
|
|
}
|
|
|
|
type VersionSegment struct {
|
|
Components []VerSegComp
|
|
}
|
|
|
|
func MustNewVersionSegmentFromString(v string) VersionSegment {
|
|
verSeg, err := NewVersionSegmentFromString(v)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("Invalid version segment '%s': %s", v, err))
|
|
}
|
|
|
|
return verSeg
|
|
}
|
|
|
|
func NewVersionSegmentFromString(v string) (VersionSegment, error) {
|
|
pieces := strings.Split(v, ".")
|
|
|
|
components := []VerSegComp{}
|
|
|
|
for _, p := range pieces {
|
|
i, matchedI, err := NewVerSegCompIntFromString(p)
|
|
if err != nil {
|
|
errMsg := fmt.Sprintf("Expected component '%s' from version segment '%s' to be a parseable integer: %s", p, v, err)
|
|
return VersionSegment{}, errors.New(errMsg)
|
|
}
|
|
|
|
if matchedI {
|
|
components = append(components, i)
|
|
} else if s, matched := NewVerSegCompStrFromString(p); matched {
|
|
components = append(components, s)
|
|
} else {
|
|
errMsg := fmt.Sprintf("Expected component '%s' from version segment '%s' to be either an integer or a formatted string", p, v)
|
|
return VersionSegment{}, errors.New(errMsg)
|
|
}
|
|
}
|
|
|
|
return VersionSegment{components}, nil
|
|
}
|
|
|
|
func NewVersionSegment(components []VerSegComp) (VersionSegment, error) {
|
|
if len(components) == 0 {
|
|
return VersionSegment{}, errors.New("Expected version segment to be build from at least one component")
|
|
}
|
|
|
|
for _, c := range components {
|
|
err := c.Validate()
|
|
if err != nil {
|
|
return VersionSegment{}, err
|
|
}
|
|
}
|
|
|
|
return VersionSegment{components}, nil
|
|
}
|
|
|
|
func (s VersionSegment) Increment() (VersionSegment, error) {
|
|
if len(s.Components) == 0 {
|
|
errMsg := "Expected version segment to have at least one component to be incremented"
|
|
return VersionSegment{}, errors.New(errMsg)
|
|
}
|
|
|
|
lastComp := s.Components[len(s.Components)-1]
|
|
|
|
lastCompInt, isInt := lastComp.(VerSegCompInt)
|
|
if !isInt {
|
|
errMsg := fmt.Sprintf("Expected version segment '%s' to have last component '%s' to be an integer", s, lastComp)
|
|
return VersionSegment{}, errors.New(errMsg)
|
|
}
|
|
|
|
copiedComponents := make([]VerSegComp, len(s.Components))
|
|
copy(copiedComponents, s.Components)
|
|
copiedComponents[len(copiedComponents)-1] = VerSegCompInt{I: lastCompInt.I + 1}
|
|
|
|
return NewVersionSegment(copiedComponents)
|
|
}
|
|
|
|
func (s VersionSegment) Copy() VersionSegment {
|
|
// Don't use constructor; assuming that original components are valid
|
|
copiedComponents := make([]VerSegComp, len(s.Components))
|
|
copy(copiedComponents, s.Components)
|
|
return VersionSegment{copiedComponents}
|
|
}
|
|
|
|
func (s VersionSegment) Empty() bool { return len(s.Components) == 0 }
|
|
|
|
func (s VersionSegment) String() string { return s.AsString() }
|
|
|
|
func (s VersionSegment) AsString() string {
|
|
result := ""
|
|
|
|
for i, c := range s.Components {
|
|
result += c.AsString()
|
|
|
|
if i < len(s.Components)-1 {
|
|
result += "."
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (s VersionSegment) Compare(other VersionSegment) int {
|
|
a := s.Components
|
|
b := other.Components
|
|
|
|
if len(a) > len(b) {
|
|
comparison := s.compareArrays(a[0:len(b)], b)
|
|
if comparison != 0 {
|
|
return comparison
|
|
}
|
|
if !s.isAllZeros(a[len(b):len(a)]) {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
if len(a) < len(b) {
|
|
comparison := s.compareArrays(a, b[0:len(a)])
|
|
if comparison != 0 {
|
|
return comparison
|
|
}
|
|
if !s.isAllZeros(b[len(a):len(b)]) {
|
|
return -1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
return s.compareArrays(a, b)
|
|
}
|
|
|
|
func (s VersionSegment) IsEq(other VersionSegment) bool { return s.Compare(other) == 0 }
|
|
func (s VersionSegment) IsGt(other VersionSegment) bool { return s.Compare(other) == 1 }
|
|
func (s VersionSegment) IsLt(other VersionSegment) bool { return s.Compare(other) == -1 }
|
|
|
|
// compareArrays compares 2 equally sized a & b
|
|
func (s VersionSegment) compareArrays(a, b []VerSegComp) int {
|
|
for i, v1 := range a {
|
|
v2 := b[i]
|
|
|
|
_, v1IsStr := v1.(VerSegCompStr)
|
|
_, v1IsInt := v1.(VerSegCompInt)
|
|
_, v2IsStr := v2.(VerSegCompStr)
|
|
_, v2IsInt := v2.(VerSegCompInt)
|
|
|
|
if v1IsStr && v2IsInt {
|
|
return 1
|
|
} else if v1IsInt && v2IsStr {
|
|
return -1
|
|
}
|
|
|
|
comparison := v1.Compare(v2)
|
|
if comparison != 0 {
|
|
return comparison
|
|
}
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func (s VersionSegment) isAllZeros(a []VerSegComp) bool {
|
|
for _, v := range a {
|
|
vTyped, ok := v.(VerSegCompInt)
|
|
if !ok || vTyped.I != 0 {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|