mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-10-31 16:13:45 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			205 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			205 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package version
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"reflect"
 | |
| 	"regexp"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| // Constraint represents a single constraint for a version, such as
 | |
| // ">= 1.0".
 | |
| type Constraint struct {
 | |
| 	f        constraintFunc
 | |
| 	check    *Version
 | |
| 	original string
 | |
| }
 | |
| 
 | |
| // Constraints is a slice of constraints. We make a custom type so that
 | |
| // we can add methods to it.
 | |
| type Constraints []*Constraint
 | |
| 
 | |
| type constraintFunc func(v, c *Version) bool
 | |
| 
 | |
| var constraintOperators map[string]constraintFunc
 | |
| 
 | |
| var constraintRegexp *regexp.Regexp
 | |
| 
 | |
| func init() {
 | |
| 	constraintOperators = map[string]constraintFunc{
 | |
| 		"":   constraintEqual,
 | |
| 		"=":  constraintEqual,
 | |
| 		"!=": constraintNotEqual,
 | |
| 		">":  constraintGreaterThan,
 | |
| 		"<":  constraintLessThan,
 | |
| 		">=": constraintGreaterThanEqual,
 | |
| 		"<=": constraintLessThanEqual,
 | |
| 		"~>": constraintPessimistic,
 | |
| 	}
 | |
| 
 | |
| 	ops := make([]string, 0, len(constraintOperators))
 | |
| 	for k := range constraintOperators {
 | |
| 		ops = append(ops, regexp.QuoteMeta(k))
 | |
| 	}
 | |
| 
 | |
| 	constraintRegexp = regexp.MustCompile(fmt.Sprintf(
 | |
| 		`^\s*(%s)\s*(%s)\s*$`,
 | |
| 		strings.Join(ops, "|"),
 | |
| 		VersionRegexpRaw))
 | |
| }
 | |
| 
 | |
| // NewConstraint will parse one or more constraints from the given
 | |
| // constraint string. The string must be a comma-separated list of
 | |
| // constraints.
 | |
| func NewConstraint(v string) (Constraints, error) {
 | |
| 	vs := strings.Split(v, ",")
 | |
| 	result := make([]*Constraint, len(vs))
 | |
| 	for i, single := range vs {
 | |
| 		c, err := parseSingle(single)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		result[i] = c
 | |
| 	}
 | |
| 
 | |
| 	return Constraints(result), nil
 | |
| }
 | |
| 
 | |
| // Check tests if a version satisfies all the constraints.
 | |
| func (cs Constraints) Check(v *Version) bool {
 | |
| 	for _, c := range cs {
 | |
| 		if !c.Check(v) {
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| // Returns the string format of the constraints
 | |
| func (cs Constraints) String() string {
 | |
| 	csStr := make([]string, len(cs))
 | |
| 	for i, c := range cs {
 | |
| 		csStr[i] = c.String()
 | |
| 	}
 | |
| 
 | |
| 	return strings.Join(csStr, ",")
 | |
| }
 | |
| 
 | |
| // Check tests if a constraint is validated by the given version.
 | |
| func (c *Constraint) Check(v *Version) bool {
 | |
| 	return c.f(v, c.check)
 | |
| }
 | |
| 
 | |
| func (c *Constraint) String() string {
 | |
| 	return c.original
 | |
| }
 | |
| 
 | |
| func parseSingle(v string) (*Constraint, error) {
 | |
| 	matches := constraintRegexp.FindStringSubmatch(v)
 | |
| 	if matches == nil {
 | |
| 		return nil, fmt.Errorf("Malformed constraint: %s", v)
 | |
| 	}
 | |
| 
 | |
| 	check, err := NewVersion(matches[2])
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return &Constraint{
 | |
| 		f:        constraintOperators[matches[1]],
 | |
| 		check:    check,
 | |
| 		original: v,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func prereleaseCheck(v, c *Version) bool {
 | |
| 	switch vPre, cPre := v.Prerelease() != "", c.Prerelease() != ""; {
 | |
| 	case cPre && vPre:
 | |
| 		// A constraint with a pre-release can only match a pre-release version
 | |
| 		// with the same base segments.
 | |
| 		return reflect.DeepEqual(c.Segments64(), v.Segments64())
 | |
| 
 | |
| 	case !cPre && vPre:
 | |
| 		// A constraint without a pre-release can only match a version without a
 | |
| 		// pre-release.
 | |
| 		return false
 | |
| 
 | |
| 	case cPre && !vPre:
 | |
| 		// OK, except with the pessimistic operator
 | |
| 	case !cPre && !vPre:
 | |
| 		// OK
 | |
| 	}
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| //-------------------------------------------------------------------
 | |
| // Constraint functions
 | |
| //-------------------------------------------------------------------
 | |
| 
 | |
| func constraintEqual(v, c *Version) bool {
 | |
| 	return v.Equal(c)
 | |
| }
 | |
| 
 | |
| func constraintNotEqual(v, c *Version) bool {
 | |
| 	return !v.Equal(c)
 | |
| }
 | |
| 
 | |
| func constraintGreaterThan(v, c *Version) bool {
 | |
| 	return prereleaseCheck(v, c) && v.Compare(c) == 1
 | |
| }
 | |
| 
 | |
| func constraintLessThan(v, c *Version) bool {
 | |
| 	return prereleaseCheck(v, c) && v.Compare(c) == -1
 | |
| }
 | |
| 
 | |
| func constraintGreaterThanEqual(v, c *Version) bool {
 | |
| 	return prereleaseCheck(v, c) && v.Compare(c) >= 0
 | |
| }
 | |
| 
 | |
| func constraintLessThanEqual(v, c *Version) bool {
 | |
| 	return prereleaseCheck(v, c) && v.Compare(c) <= 0
 | |
| }
 | |
| 
 | |
| func constraintPessimistic(v, c *Version) bool {
 | |
| 	// Using a pessimistic constraint with a pre-release, restricts versions to pre-releases
 | |
| 	if !prereleaseCheck(v, c) || (c.Prerelease() != "" && v.Prerelease() == "") {
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	// If the version being checked is naturally less than the constraint, then there
 | |
| 	// is no way for the version to be valid against the constraint
 | |
| 	if v.LessThan(c) {
 | |
| 		return false
 | |
| 	}
 | |
| 	// We'll use this more than once, so grab the length now so it's a little cleaner
 | |
| 	// to write the later checks
 | |
| 	cs := len(c.segments)
 | |
| 
 | |
| 	// If the version being checked has less specificity than the constraint, then there
 | |
| 	// is no way for the version to be valid against the constraint
 | |
| 	if cs > len(v.segments) {
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	// Check the segments in the constraint against those in the version. If the version
 | |
| 	// being checked, at any point, does not have the same values in each index of the
 | |
| 	// constraints segments, then it cannot be valid against the constraint.
 | |
| 	for i := 0; i < c.si-1; i++ {
 | |
| 		if v.segments[i] != c.segments[i] {
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Check the last part of the segment in the constraint. If the version segment at
 | |
| 	// this index is less than the constraints segment at this index, then it cannot
 | |
| 	// be valid against the constraint
 | |
| 	if c.segments[cs-1] > v.segments[cs-1] {
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	// If nothing has rejected the version by now, it's valid
 | |
| 	return true
 | |
| }
 | 
