blob: b6e8fdab450fabd82cea9651ea6be35b57fadf9c [file] [log] [blame]
Serge Bazanskicbd02be2021-09-24 13:39:12 +02001package toolbase
2
3import (
4 "fmt"
5 "regexp"
6 "strings"
7)
8
9// BazelLabel is a label, as defined by Bazel's documentation:
10//
11// https://docs.bazel.build/versions/main/skylark/lib/Label.html
12type BazelLabel struct {
13 WorkspaceName string
14 PackagePath []string
15 Name string
16}
17
18func (b BazelLabel) Package() string {
19 return strings.Join(b.PackagePath, "/")
20}
21
22func (b BazelLabel) String() string {
23 return fmt.Sprintf("@%s//%s:%s", b.WorkspaceName, b.Package(), b.Name)
24}
25
26var (
27 // reLabel splits a Bazel label into a workspace name (if set) and a
28 // workspace root relative package/name.
29 reLabel = regexp.MustCompile(`^(@[^:/]+)?//(.+)$`)
30 // rePathPart matches valid label path parts.
31 rePathPart = regexp.MustCompile(`^[^:/]+$`)
32)
33
34// ParseBazelLabel converts parses a string representation of a Bazel Label. If
35// the given representation is invalid or for some other reason unparseable,
36// nil is returned.
37func ParseBazelLabel(s string) *BazelLabel {
38 res := BazelLabel{
39 WorkspaceName: "dev_source_monogon",
40 }
41
42 // Split label into workspace name (if set) and a workspace root relative
43 // package/name.
44 m := reLabel.FindStringSubmatch(s)
45 if m == nil {
46 return nil
47 }
48 packageRel := m[2]
49 if m[1] != "" {
50 res.WorkspaceName = m[1][1:]
51 }
52
53 // Split path by ':', which is the target name delimiter. If it appears
54 // exactly once, interpret everything to its right as the target name.
55 targetSplit := strings.Split(packageRel, ":")
56 switch len(targetSplit) {
57 case 1:
58 case 2:
59 packageRel = targetSplit[0]
60 res.Name = targetSplit[1]
61 if !rePathPart.MatchString(res.Name) {
62 return nil
63 }
64 default:
65 return nil
66 }
67
68 // Split the package path by /, and if the name was not explicitly given,
69 // use the last element of the package path.
70 if packageRel == "" {
71 res.PackagePath = nil
72 } else {
73 res.PackagePath = strings.Split(packageRel, "/")
74 }
75 if res.Name == "" {
76 res.Name = res.PackagePath[len(res.PackagePath)-1]
77 }
78
79 // Ensure all parts of the package path are valid.
80 for _, p := range res.PackagePath {
81 if !rePathPart.MatchString(p) {
82 return nil
83 }
84 }
85
86 return &res
87}