github.com/quay/claircore@v1.5.28/version.go (about) 1 package claircore 2 3 import ( 4 "bytes" 5 "strconv" 6 "strings" 7 8 "github.com/Masterminds/semver" 9 ) 10 11 // Version describes a revision of some sort that is ordered correctly within 12 // its "Kind". 13 // 14 // Versions of different kinds do not have any sensible ordering. 15 type Version struct { 16 Kind string 17 V [10]int32 18 } 19 20 // VersionSort returns a function suitable for passing to sort.Slice or 21 // sort.SliceStable. 22 func VersionSort(vs []Version) func(int, int) bool { 23 return func(i, j int) bool { return vs[i].Compare(&vs[j]) == -1 } 24 } 25 26 // FromSemver is the SemVer to claircore.Version mapping used by this package. 27 func FromSemver(v *semver.Version) (out Version) { 28 out.Kind = `semver` 29 // Leave a leading epoch, for good measure. 30 out.V[1] = int32(v.Major()) 31 out.V[2] = int32(v.Minor()) 32 out.V[3] = int32(v.Patch()) 33 return out 34 } 35 36 // MarshalText implments encoding.TextMarshaler. 37 func (v *Version) MarshalText() ([]byte, error) { 38 if v.Kind == "" { 39 return []byte{}, nil 40 } 41 var buf bytes.Buffer 42 b := make([]byte, 0, 16) // 16 byte wide scratch buffer 43 buf.WriteString(v.Kind) 44 buf.WriteByte(':') 45 for i := 0; i < 10; i++ { 46 if i != 0 { 47 buf.WriteByte('.') 48 } 49 buf.Write(strconv.AppendInt(b, int64(v.V[i]), 10)) 50 } 51 return buf.Bytes(), nil 52 } 53 54 // UnmarshalText implments encoding.TextUnmarshaler. 55 func (v *Version) UnmarshalText(text []byte) (err error) { 56 idx := bytes.IndexByte(text, ':') 57 if idx == -1 { 58 return nil 59 } 60 if v == nil { 61 *v = Version{} 62 } 63 v.Kind = string(text[:idx]) 64 var n int64 65 for i, b := range bytes.Split(text[idx+1:], []byte(".")) { 66 n, err = strconv.ParseInt(string(b), 10, 32) 67 if err != nil { 68 return err 69 } 70 v.V[i] = int32(n) 71 } 72 return nil 73 } 74 75 func (v *Version) String() string { 76 var buf strings.Builder 77 b := make([]byte, 0, 16) // 16 byte wide scratch buffer 78 79 if v.V[0] != 0 { 80 buf.Write(strconv.AppendInt(b, int64(v.V[0]), 10)) 81 buf.WriteByte('!') 82 } 83 var f, l int 84 for i := 1; i < 10; i++ { 85 if v.V[i] != 0 { 86 if f == 0 { 87 f = i 88 } 89 l = i 90 } 91 } 92 // If we didn't set the offsets in the above loop, bump to make them 93 // absolute to the version array. 94 if f == 0 { 95 f++ 96 } 97 if l == 0 { 98 l++ 99 } 100 for i, n := range v.V[f : l+1] { 101 if i != 0 { 102 buf.WriteByte('.') 103 } 104 buf.Write(strconv.AppendInt(b, int64(n), 10)) 105 } 106 107 return buf.String() 108 } 109 110 // Compare returns an integer describing the relationship of two Versions. 111 // 112 // The result will be 0 if a==b, -1 if a < b, and +1 if a > b. If the Versions 113 // are of different kinds, the Kinds will be compared lexographically. 114 func (v *Version) Compare(x *Version) int { 115 if v.Kind != x.Kind { 116 return strings.Compare(v.Kind, x.Kind) 117 } 118 for i := 0; i < 10; i++ { 119 if v.V[i] > x.V[i] { 120 return 1 121 } 122 if v.V[i] < x.V[i] { 123 return -1 124 } 125 } 126 return 0 127 } 128 129 // Range is a half-open interval of two Versions. 130 // 131 // In the usual notation, it is: [Lower, Upper) 132 type Range struct { 133 Lower Version `json:"["` 134 Upper Version `json:")"` 135 } 136 137 // Contains reports whether the Version falls within the Range. 138 func (r *Range) Contains(v *Version) bool { 139 if r == nil { 140 return false 141 } 142 // Lower <= v && Upper > v 143 return r.Lower.Compare(v) != 1 && r.Upper.Compare(v) == 1 144 }