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  }