-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdependency.go
179 lines (137 loc) · 4.25 KB
/
dependency.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package k6deps
import (
"bytes"
"errors"
"fmt"
"io"
"regexp"
"github.com/Masterminds/semver/v3"
)
const (
// ConstraintsAny is a wildcard constraint that any version matches.
ConstraintsAny = "*"
// NameK6 is the name of the k6 dependency, its value is "k6".
NameK6 = "k6"
defaultConstraintsString = ConstraintsAny
)
//nolint:gochecknoglobals
var (
ErrConstraints = errors.New("constraints error")
ErrDependency = errors.New("dependency error")
defaultConstraints, _ = semver.NewConstraint(defaultConstraintsString)
srcDependency = `(?P<name>[0-9a-zA-Z/@_-]+) *(?P<constraints>` + srcConstraint + `)?`
reDependency = regexp.MustCompile(srcDependency)
idxDependencyName = reDependency.SubexpIndex("name")
idxDependencyConstraints = reDependency.SubexpIndex("constraints")
)
// Dependency contains the properties of a k6 dependency (extension or k6 core).
type Dependency struct {
// Name is the name of the dependency.
Name string `json:"name,omitempty"`
// Constraints contains the version constraints of the dependency.
Constraints *semver.Constraints `json:"constraints,omitempty"`
}
// NewDependency creates a new Dependency instance with the given name.
// If the constraints parameter is not empty, it will be parsed as version constraints.
func NewDependency(name, constraints string) (*Dependency, error) {
var err error
dep := new(Dependency)
dep.Name = name
if len(constraints) != 0 {
if dep.Constraints, err = semver.NewConstraint(constraints); err != nil {
return nil, fmt.Errorf("%w: %s", ErrConstraints, err.Error())
}
}
return dep, nil
}
// GetConstraints returns Constraints or the default constraints ("*") if Constraints is nil
func (dep *Dependency) GetConstraints() *semver.Constraints {
if dep.Constraints == nil {
return defaultConstraints
}
return dep.Constraints
}
// MarshalText marshals the dependency into a single-line text format.
// For example: k6/x/faker>0.1.0
func (dep *Dependency) MarshalText() ([]byte, error) {
var buff bytes.Buffer
if err := dep.marshalText(&buff); err != nil {
return nil, err
}
return buff.Bytes(), nil
}
func (dep *Dependency) marshalText(w io.Writer) error {
_, err := io.WriteString(w, dep.Name)
if err != nil {
return err
}
_, err = io.WriteString(w, dep.GetConstraints().String())
return err
}
// MarshalJS marshals the dependency into a one-line JavaScript string directive format.
// For example: "us k6 with k6/x/faker>0.1.0";
func (dep *Dependency) MarshalJS() ([]byte, error) {
var buff bytes.Buffer
if err := dep.marshalJS(&buff); err != nil {
return nil, err
}
return buff.Bytes(), nil
}
func (dep *Dependency) marshalJS(w io.Writer) error {
_, err := io.WriteString(w, `"use k6`)
if err != nil {
return err
}
if dep.Name != NameK6 {
_, err = io.WriteString(w, " with ")
if err != nil {
return err
}
_, err = io.WriteString(w, dep.Name)
if err != nil {
return err
}
}
_, err = io.WriteString(w, dep.GetConstraints().String())
if err != nil {
return err
}
_, err = io.WriteString(w, `";`)
return err
}
// UnmarshalText parses the one-line text dependency format into the *dep variable.
func (dep *Dependency) UnmarshalText(text []byte) error {
match := reDependency.FindSubmatch(text)
if match == nil {
return fmt.Errorf("%w: invalid text format: %s", ErrDependency, string(text))
}
dep.Name = string(match[idxDependencyName])
var err error
dep.Constraints, err = semver.NewConstraint(string(match[idxDependencyConstraints]))
if err != nil {
return fmt.Errorf("%w: %s", ErrConstraints, err.Error())
}
return nil
}
// String converts the dependency to displayable text format.
// The format is the same as that used by MarshalText.
func (dep *Dependency) String() string {
text, _ := dep.MarshalText()
return string(text)
}
func (dep *Dependency) update(from *Dependency) error {
fromString := from.GetConstraints().String()
if fromString == defaultConstraintsString {
return nil
}
depString := dep.GetConstraints().String()
if depString == defaultConstraintsString {
dep.Constraints = from.Constraints
return nil
}
if depString == fromString {
return nil
}
return fmt.Errorf("%w: %s has conflicting constraints:\n %s\n %s",
ErrConstraints, dep.Name, dep.GetConstraints(), from.GetConstraints())
}