github.com/golang/dep@v0.5.4/gps/constraint.go (about)

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gps
     6  
     7  import (
     8  	"fmt"
     9  	"sort"
    10  
    11  	"github.com/Masterminds/semver"
    12  	"github.com/golang/dep/gps/internal/pb"
    13  )
    14  
    15  var (
    16  	none = noneConstraint{}
    17  	any  = anyConstraint{}
    18  )
    19  
    20  // A Constraint provides structured limitations on the versions that are
    21  // admissible for a given project.
    22  //
    23  // As with Version, it has a private method because the gps's internal
    24  // implementation of the problem is complete, and the system relies on type
    25  // magic to operate.
    26  type Constraint interface {
    27  	fmt.Stringer
    28  
    29  	// ImpliedCaretString converts the Constraint to a string in the same manner
    30  	// as String(), but treats the empty operator as equivalent to ^, rather
    31  	// than =.
    32  	//
    33  	// In the same way that String() is the inverse of NewConstraint(), this
    34  	// method is the inverse of NewSemverConstraintIC().
    35  	ImpliedCaretString() string
    36  
    37  	// Matches indicates if the provided Version is allowed by the Constraint.
    38  	Matches(Version) bool
    39  
    40  	// MatchesAny indicates if the intersection of the Constraint with the
    41  	// provided Constraint would yield a Constraint that could allow *any*
    42  	// Version.
    43  	MatchesAny(Constraint) bool
    44  
    45  	// Intersect computes the intersection of the Constraint with the provided
    46  	// Constraint.
    47  	Intersect(Constraint) Constraint
    48  
    49  	// typedString emits the normal stringified representation of the provided
    50  	// constraint, prefixed with a string that uniquely identifies the type of
    51  	// the constraint.
    52  	//
    53  	// It also forces Constraint to be a private/sealed interface, which is a
    54  	// design goal of the system.
    55  	typedString() string
    56  
    57  	// copyTo copies fields into a serializable representation which can be
    58  	// converted back into an identical Constraint with constraintFromCache.
    59  	copyTo(*pb.Constraint)
    60  
    61  	// identical returns true if the constraints are identical.
    62  	//
    63  	// Identical Constraints behave identically for all methods defined by the
    64  	// interface. A Constraint is always identical to itself.
    65  	//
    66  	// Constraints serialized for caching are de-serialized into identical instances.
    67  	identical(Constraint) bool
    68  }
    69  
    70  // constraintFromCache returns a Constraint identical to the one which produced m.
    71  func constraintFromCache(m *pb.Constraint) (Constraint, error) {
    72  	switch m.Type {
    73  	case pb.Constraint_Revision:
    74  		return Revision(m.Value), nil
    75  	case pb.Constraint_Branch:
    76  		return NewBranch(m.Value), nil
    77  	case pb.Constraint_DefaultBranch:
    78  		return newDefaultBranch(m.Value), nil
    79  	case pb.Constraint_Version:
    80  		return plainVersion(m.Value), nil
    81  	case pb.Constraint_Semver:
    82  		return NewSemverConstraint(m.Value)
    83  
    84  	default:
    85  		return nil, fmt.Errorf("unrecognized Constraint type: %#v", m)
    86  	}
    87  }
    88  
    89  // unpairedVersionFromCache returns an UnpairedVersion identical to the one which produced m.
    90  func unpairedVersionFromCache(m *pb.Constraint) (UnpairedVersion, error) {
    91  	switch m.Type {
    92  	case pb.Constraint_Branch:
    93  		return NewBranch(m.Value), nil
    94  	case pb.Constraint_DefaultBranch:
    95  		return newDefaultBranch(m.Value), nil
    96  	case pb.Constraint_Version:
    97  		return plainVersion(m.Value), nil
    98  	case pb.Constraint_Semver:
    99  		sv, err := semver.NewVersion(m.Value)
   100  		if err != nil {
   101  			return nil, err
   102  		}
   103  		return semVersion{sv: sv}, nil
   104  
   105  	default:
   106  		return nil, fmt.Errorf("unrecognized UnpairedVersion type: %#v", m)
   107  	}
   108  }
   109  
   110  // NewSemverConstraint attempts to construct a semver Constraint object from the
   111  // input string.
   112  //
   113  // If the input string cannot be made into a valid semver Constraint, an error
   114  // is returned.
   115  func NewSemverConstraint(body string) (Constraint, error) {
   116  	c, err := semver.NewConstraint(body)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  	// If we got a simple semver.Version, simplify by returning our
   121  	// corresponding type
   122  	if sv, ok := c.(semver.Version); ok {
   123  		return semVersion{sv: sv}, nil
   124  	}
   125  	return semverConstraint{c: c}, nil
   126  }
   127  
   128  // NewSemverConstraintIC attempts to construct a semver Constraint object from the
   129  // input string, defaulting to a caret, ^, when no operator is specified. Put
   130  // differently, ^ is the default operator for NewSemverConstraintIC, while =
   131  // is the default operator for NewSemverConstraint.
   132  //
   133  // If the input string cannot be made into a valid semver Constraint, an error
   134  // is returned.
   135  func NewSemverConstraintIC(body string) (Constraint, error) {
   136  	c, err := semver.NewConstraintIC(body)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  	// If we got a simple semver.Version, simplify by returning our
   141  	// corresponding type
   142  	if sv, ok := c.(semver.Version); ok {
   143  		return semVersion{sv: sv}, nil
   144  	}
   145  	return semverConstraint{c: c}, nil
   146  }
   147  
   148  type semverConstraint struct {
   149  	c semver.Constraint
   150  }
   151  
   152  func (c semverConstraint) String() string {
   153  	return c.c.String()
   154  }
   155  
   156  // ImpliedCaretString converts the Constraint to a string in the same manner
   157  // as String(), but treats the empty operator as equivalent to ^, rather
   158  // than =.
   159  //
   160  // In the same way that String() is the inverse of NewConstraint(), this
   161  // method is the inverse of NewSemverConstraintIC().
   162  func (c semverConstraint) ImpliedCaretString() string {
   163  	return c.c.ImpliedCaretString()
   164  }
   165  
   166  func (c semverConstraint) typedString() string {
   167  	return fmt.Sprintf("svc-%s", c.c.String())
   168  }
   169  
   170  func (c semverConstraint) Matches(v Version) bool {
   171  	switch tv := v.(type) {
   172  	case semVersion:
   173  		return c.c.Matches(tv.sv) == nil
   174  	case versionPair:
   175  		if tv2, ok := tv.v.(semVersion); ok {
   176  			return c.c.Matches(tv2.sv) == nil
   177  		}
   178  	}
   179  
   180  	return false
   181  }
   182  
   183  func (c semverConstraint) MatchesAny(c2 Constraint) bool {
   184  	return c.Intersect(c2) != none
   185  }
   186  
   187  func (c semverConstraint) Intersect(c2 Constraint) Constraint {
   188  	switch tc := c2.(type) {
   189  	case anyConstraint:
   190  		return c
   191  	case semverConstraint:
   192  		rc := c.c.Intersect(tc.c)
   193  		if !semver.IsNone(rc) {
   194  			return semverConstraint{c: rc}
   195  		}
   196  	case semVersion:
   197  		rc := c.c.Intersect(tc.sv)
   198  		if !semver.IsNone(rc) {
   199  			// If single version intersected with constraint, we know the result
   200  			// must be the single version, so just return it back out
   201  			return c2
   202  		}
   203  	case versionPair:
   204  		if tc2, ok := tc.v.(semVersion); ok {
   205  			rc := c.c.Intersect(tc2.sv)
   206  			if !semver.IsNone(rc) {
   207  				// same reasoning as previous case
   208  				return c2
   209  			}
   210  		}
   211  	}
   212  
   213  	return none
   214  }
   215  
   216  func (c semverConstraint) identical(c2 Constraint) bool {
   217  	sc2, ok := c2.(semverConstraint)
   218  	if !ok {
   219  		return false
   220  	}
   221  	return c.c.String() == sc2.c.String()
   222  }
   223  
   224  func (c semverConstraint) copyTo(msg *pb.Constraint) {
   225  	msg.Type = pb.Constraint_Semver
   226  	msg.Value = c.String()
   227  }
   228  
   229  // IsAny indicates if the provided constraint is the wildcard "Any" constraint.
   230  func IsAny(c Constraint) bool {
   231  	_, ok := c.(anyConstraint)
   232  	return ok
   233  }
   234  
   235  // Any returns a constraint that will match anything.
   236  func Any() Constraint {
   237  	return anyConstraint{}
   238  }
   239  
   240  // anyConstraint is an unbounded constraint - it matches all other types of
   241  // constraints. It mirrors the behavior of the semver package's any type.
   242  type anyConstraint struct{}
   243  
   244  func (anyConstraint) String() string {
   245  	return "*"
   246  }
   247  
   248  func (anyConstraint) ImpliedCaretString() string {
   249  	return "*"
   250  }
   251  
   252  func (anyConstraint) typedString() string {
   253  	return "any-*"
   254  }
   255  
   256  func (anyConstraint) Matches(Version) bool {
   257  	return true
   258  }
   259  
   260  func (anyConstraint) MatchesAny(Constraint) bool {
   261  	return true
   262  }
   263  
   264  func (anyConstraint) Intersect(c Constraint) Constraint {
   265  	return c
   266  }
   267  
   268  func (anyConstraint) identical(c Constraint) bool {
   269  	return IsAny(c)
   270  }
   271  
   272  func (anyConstraint) copyTo(*pb.Constraint) {
   273  	panic("anyConstraint should never be serialized; it is solver internal-only")
   274  }
   275  
   276  // noneConstraint is the empty set - it matches no versions. It mirrors the
   277  // behavior of the semver package's none type.
   278  type noneConstraint struct{}
   279  
   280  func (noneConstraint) String() string {
   281  	return ""
   282  }
   283  
   284  func (noneConstraint) ImpliedCaretString() string {
   285  	return ""
   286  }
   287  
   288  func (noneConstraint) typedString() string {
   289  	return "none-"
   290  }
   291  
   292  func (noneConstraint) Matches(Version) bool {
   293  	return false
   294  }
   295  
   296  func (noneConstraint) MatchesAny(Constraint) bool {
   297  	return false
   298  }
   299  
   300  func (noneConstraint) Intersect(Constraint) Constraint {
   301  	return none
   302  }
   303  
   304  func (noneConstraint) identical(c Constraint) bool {
   305  	_, ok := c.(noneConstraint)
   306  	return ok
   307  }
   308  
   309  func (noneConstraint) copyTo(*pb.Constraint) {
   310  	panic("noneConstraint should never be serialized; it is solver internal-only")
   311  }
   312  
   313  // A ProjectConstraint combines a ProjectIdentifier with a Constraint. It
   314  // indicates that, if packages contained in the ProjectIdentifier enter the
   315  // depgraph, they must do so at a version that is allowed by the Constraint.
   316  type ProjectConstraint struct {
   317  	Ident      ProjectIdentifier
   318  	Constraint Constraint
   319  }
   320  
   321  // ProjectConstraints is a map of projects, as identified by their import path
   322  // roots (ProjectRoots) to the corresponding ProjectProperties.
   323  //
   324  // They are the standard form in which Manifests declare their required
   325  // dependency properties - constraints and network locations - as well as the
   326  // form in which RootManifests declare their overrides.
   327  type ProjectConstraints map[ProjectRoot]ProjectProperties
   328  
   329  type workingConstraint struct {
   330  	Ident                     ProjectIdentifier
   331  	Constraint                Constraint
   332  	overrNet, overrConstraint bool
   333  }
   334  
   335  func pcSliceToMap(l []ProjectConstraint, r ...[]ProjectConstraint) ProjectConstraints {
   336  	final := make(ProjectConstraints)
   337  
   338  	for _, pc := range l {
   339  		final[pc.Ident.ProjectRoot] = ProjectProperties{
   340  			Source:     pc.Ident.Source,
   341  			Constraint: pc.Constraint,
   342  		}
   343  	}
   344  
   345  	for _, pcs := range r {
   346  		for _, pc := range pcs {
   347  			if pp, exists := final[pc.Ident.ProjectRoot]; exists {
   348  				// Technically this should be done through a bridge for
   349  				// cross-version-type matching...but this is a one off for root and
   350  				// that's just ridiculous for this.
   351  				pp.Constraint = pp.Constraint.Intersect(pc.Constraint)
   352  				final[pc.Ident.ProjectRoot] = pp
   353  			} else {
   354  				final[pc.Ident.ProjectRoot] = ProjectProperties{
   355  					Source:     pc.Ident.Source,
   356  					Constraint: pc.Constraint,
   357  				}
   358  			}
   359  		}
   360  	}
   361  
   362  	return final
   363  }
   364  
   365  // overrideAll treats the receiver ProjectConstraints map as a set of override
   366  // instructions, and applies overridden values to the ProjectConstraints.
   367  //
   368  // A slice of workingConstraint is returned, allowing differentiation between
   369  // values that were or were not overridden.
   370  func (m ProjectConstraints) overrideAll(pcm ProjectConstraints) (out []workingConstraint) {
   371  	out = make([]workingConstraint, len(pcm))
   372  	k := 0
   373  	for pr, pp := range pcm {
   374  		out[k] = m.override(pr, pp)
   375  		k++
   376  	}
   377  
   378  	sort.SliceStable(out, func(i, j int) bool {
   379  		return out[i].Ident.Less(out[j].Ident)
   380  	})
   381  	return
   382  }
   383  
   384  // override replaces a single ProjectConstraint with a workingConstraint,
   385  // overriding its values if a corresponding entry exists in the
   386  // ProjectConstraints map.
   387  func (m ProjectConstraints) override(pr ProjectRoot, pp ProjectProperties) workingConstraint {
   388  	wc := workingConstraint{
   389  		Ident: ProjectIdentifier{
   390  			ProjectRoot: pr,
   391  			Source:      pp.Source,
   392  		},
   393  		Constraint: pp.Constraint,
   394  	}
   395  
   396  	if opp, has := m[pr]; has {
   397  		// The rule for overrides is that *any* non-zero value for the prop
   398  		// should be considered an override, even if it's equal to what's
   399  		// already there.
   400  		if opp.Constraint != nil {
   401  			wc.Constraint = opp.Constraint
   402  			wc.overrConstraint = true
   403  		}
   404  
   405  		// This may appear incorrect, because the solver encodes meaning into
   406  		// the empty string for NetworkName (it means that it would use the
   407  		// import path by default, but could be coerced into using an alternate
   408  		// URL). However, that 'coercion' can only happen if there's a
   409  		// disagreement between projects on where a dependency should be sourced
   410  		// from. Such disagreement is exactly what overrides preclude, so
   411  		// there's no need to preserve the meaning of "" here - thus, we can
   412  		// treat it as a zero value and ignore it, rather than applying it.
   413  		if opp.Source != "" {
   414  			wc.Ident.Source = opp.Source
   415  			wc.overrNet = true
   416  		}
   417  	}
   418  
   419  	return wc
   420  }