1package discovery
2
3import (
4	"sort"
5
6	version "github.com/hashicorp/go-version"
7)
8
9// A ConstraintStr is a string containing a possibly-invalid representation
10// of a version constraint provided in configuration. Call Parse on it to
11// obtain a real Constraint object, or discover that it is invalid.
12type ConstraintStr string
13
14// Parse transforms a ConstraintStr into a Constraints if it is
15// syntactically valid. If it isn't then an error is returned instead.
16func (s ConstraintStr) Parse() (Constraints, error) {
17	raw, err := version.NewConstraint(string(s))
18	if err != nil {
19		return Constraints{}, err
20	}
21	return Constraints{raw}, nil
22}
23
24// MustParse is like Parse but it panics if the constraint string is invalid.
25func (s ConstraintStr) MustParse() Constraints {
26	ret, err := s.Parse()
27	if err != nil {
28		panic(err)
29	}
30	return ret
31}
32
33// Constraints represents a set of versions which any given Version is either
34// a member of or not.
35type Constraints struct {
36	raw version.Constraints
37}
38
39// NewConstraints creates a Constraints based on a version.Constraints.
40func NewConstraints(c version.Constraints) Constraints {
41	return Constraints{c}
42}
43
44// AllVersions is a Constraints containing all versions
45var AllVersions Constraints
46
47func init() {
48	AllVersions = Constraints{
49		raw: make(version.Constraints, 0),
50	}
51}
52
53// Allows returns true if the given version permitted by the receiving
54// constraints set.
55func (s Constraints) Allows(v Version) bool {
56	return s.raw.Check(v.raw)
57}
58
59// Append combines the receiving set with the given other set to produce
60// a set that is the intersection of both sets, which is to say that resulting
61// constraints contain only the versions that are members of both.
62func (s Constraints) Append(other Constraints) Constraints {
63	raw := make(version.Constraints, 0, len(s.raw)+len(other.raw))
64
65	// Since "raw" is a list of constraints that remove versions from the set,
66	// "Intersection" is implemented by concatenating together those lists,
67	// thus leaving behind only the versions not removed by either list.
68	raw = append(raw, s.raw...)
69	raw = append(raw, other.raw...)
70
71	// while the set is unordered, we sort these lexically for consistent output
72	sort.Slice(raw, func(i, j int) bool {
73		return raw[i].String() < raw[j].String()
74	})
75
76	return Constraints{raw}
77}
78
79// String returns a string representation of the set members as a set
80// of range constraints.
81func (s Constraints) String() string {
82	return s.raw.String()
83}
84
85// Unconstrained returns true if and only if the receiver is an empty
86// constraint set.
87func (s Constraints) Unconstrained() bool {
88	return len(s.raw) == 0
89}
90