1
15
16
17
18
19
20 package label
21
22 import (
23 "fmt"
24 "log"
25 "path"
26 "regexp"
27 "strings"
28
29 "github.com/bazelbuild/bazel-gazelle/pathtools"
30 bzl "github.com/bazelbuild/buildtools/build"
31 )
32
33
34
35
36 type Label struct {
37
38
39 Repo string
40
41
42
43 Pkg string
44
45
46
47
48
49 Name string
50
51
52
53 Relative bool
54 }
55
56
57 func New(repo, pkg, name string) Label {
58 return Label{Repo: repo, Pkg: pkg, Name: name}
59 }
60
61
62
63 var NoLabel = Label{}
64
65 var (
66
67 labelRepoRegexp = regexp.MustCompile(`^@$|^[A-Za-z0-9_.-][A-Za-z0-9_.~-]*$`)
68
69
70
71
72
73
74
75 labelPkgRegexp = regexp.MustCompile(`^[\x20-\x39\x3B-\x5B\x5D-\x7E]*$`)
76 labelNameRegexp = labelPkgRegexp
77 )
78
79
80
81 func Parse(s string) (Label, error) {
82 origStr := s
83
84 relative := true
85 var repo string
86
87 if strings.HasPrefix(s, "@@") {
88 s = s[len("@"):]
89 }
90 if strings.HasPrefix(s, "@") {
91 relative = false
92 endRepo := strings.Index(s, "//")
93 if endRepo > len("@") {
94 repo = s[len("@"):endRepo]
95 s = s[endRepo:]
96
97
98 } else if endRepo == len("@") {
99 repo = s[:len("@")]
100 s = s[len("@"):]
101 } else {
102 repo = s[len("@"):]
103 s = "//:" + repo
104 }
105 if !labelRepoRegexp.MatchString(repo) {
106 return NoLabel, fmt.Errorf("label parse error: repository has invalid characters: %q", origStr)
107 }
108 }
109
110 var pkg string
111 if strings.HasPrefix(s, "//") {
112 relative = false
113 endPkg := strings.Index(s, ":")
114 if endPkg < 0 {
115 pkg = s[len("//"):]
116 s = ""
117 } else {
118 pkg = s[len("//"):endPkg]
119 s = s[endPkg:]
120 }
121 if !labelPkgRegexp.MatchString(pkg) {
122 return NoLabel, fmt.Errorf("label parse error: package has invalid characters: %q", origStr)
123 }
124 }
125
126 if s == ":" {
127 return NoLabel, fmt.Errorf("label parse error: empty name: %q", origStr)
128 }
129 name := strings.TrimPrefix(s, ":")
130 if !labelNameRegexp.MatchString(name) {
131 return NoLabel, fmt.Errorf("label parse error: name has invalid characters: %q", origStr)
132 }
133
134 if pkg == "" && name == "" {
135 return NoLabel, fmt.Errorf("label parse error: empty package and name: %q", origStr)
136 }
137 if name == "" {
138 name = path.Base(pkg)
139 }
140
141 return Label{
142 Repo: repo,
143 Pkg: pkg,
144 Name: name,
145 Relative: relative,
146 }, nil
147 }
148
149 func (l Label) String() string {
150 if l.Relative {
151 return fmt.Sprintf(":%s", l.Name)
152 }
153
154 var repo string
155 if l.Repo != "" && l.Repo != "@" {
156 repo = fmt.Sprintf("@%s", l.Repo)
157 } else {
158
159
160 repo = l.Repo
161 }
162
163 if path.Base(l.Pkg) == l.Name {
164 return fmt.Sprintf("%s//%s", repo, l.Pkg)
165 }
166 return fmt.Sprintf("%s//%s:%s", repo, l.Pkg, l.Name)
167 }
168
169
170
171
172 func (l Label) Abs(repo, pkg string) Label {
173 if !l.Relative {
174 return l
175 }
176 return Label{Repo: repo, Pkg: pkg, Name: l.Name}
177 }
178
179
180
181
182 func (l Label) Rel(repo, pkg string) Label {
183 if l.Relative || l.Repo != repo {
184 return l
185 }
186 if l.Pkg == pkg {
187 return Label{Name: l.Name, Relative: true}
188 }
189 return Label{Pkg: l.Pkg, Name: l.Name}
190 }
191
192
193
194 func (l Label) Equal(other Label) bool {
195 return l.Repo == other.Repo &&
196 l.Pkg == other.Pkg &&
197 l.Name == other.Name &&
198 l.Relative == other.Relative
199 }
200
201
202
203 func (l Label) Contains(other Label) bool {
204 if l.Relative {
205 log.Panicf("l must not be relative: %s", l)
206 }
207 if other.Relative {
208 log.Panicf("other must not be relative: %s", other)
209 }
210 result := l.Repo == other.Repo && pathtools.HasPrefix(other.Pkg, l.Pkg)
211 return result
212 }
213
214 func (l Label) BzlExpr() bzl.Expr {
215 return &bzl.StringExpr {
216 Value: l.String(),
217 }
218 }
219
220 var nonWordRe = regexp.MustCompile(`\W+`)
221
222
223
224 func ImportPathToBazelRepoName(importpath string) string {
225 importpath = strings.ToLower(importpath)
226 components := strings.Split(importpath, "/")
227 labels := strings.Split(components[0], ".")
228 reversed := make([]string, 0, len(labels)+len(components)-1)
229 for i := range labels {
230 l := labels[len(labels)-i-1]
231 reversed = append(reversed, l)
232 }
233 repo := strings.Join(append(reversed, components[1:]...), ".")
234 return nonWordRe.ReplaceAllString(repo, "_")
235 }
236
View as plain text