/vendor/github.com/bazelbuild/bazel-gazelle/internal/label/label.go

https://gitlab.com/unofficial-mirrors/kubernetes · Go · 163 lines · 120 code · 20 blank · 23 comment · 31 complexity · 2195cf5e176609ad592b365eadd27b13 MD5 · raw file

  1. /* Copyright 2016 The Bazel Authors. All rights reserved.
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package label
  13. import (
  14. "fmt"
  15. "log"
  16. "path"
  17. "regexp"
  18. "strings"
  19. "github.com/bazelbuild/bazel-gazelle/internal/pathtools"
  20. )
  21. // A Label represents a label of a build target in Bazel.
  22. type Label struct {
  23. Repo, Pkg, Name string
  24. Relative bool
  25. }
  26. func New(repo, pkg, name string) Label {
  27. return Label{Repo: repo, Pkg: pkg, Name: name}
  28. }
  29. // NoLabel is the nil value of Label. It is not a valid label and may be
  30. // returned when an error occurs.
  31. var NoLabel = Label{}
  32. var (
  33. labelRepoRegexp = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_]*$`)
  34. labelPkgRegexp = regexp.MustCompile(`^[A-Za-z0-9/._-]*$`)
  35. labelNameRegexp = regexp.MustCompile(`^[A-Za-z0-9_/.+=,@~-]*$`)
  36. )
  37. // Parse reads a label from a string.
  38. // See https://docs.bazel.build/versions/master/build-ref.html#lexi.
  39. func Parse(s string) (Label, error) {
  40. origStr := s
  41. relative := true
  42. var repo string
  43. if strings.HasPrefix(s, "@") {
  44. relative = false
  45. endRepo := strings.Index(s, "//")
  46. if endRepo < 0 {
  47. return NoLabel, fmt.Errorf("label parse error: repository does not end with '//': %q", origStr)
  48. }
  49. repo = s[len("@"):endRepo]
  50. if !labelRepoRegexp.MatchString(repo) {
  51. return NoLabel, fmt.Errorf("label parse error: repository has invalid characters: %q", origStr)
  52. }
  53. s = s[endRepo:]
  54. }
  55. var pkg string
  56. if strings.HasPrefix(s, "//") {
  57. relative = false
  58. endPkg := strings.Index(s, ":")
  59. if endPkg < 0 {
  60. pkg = s[len("//"):]
  61. s = ""
  62. } else {
  63. pkg = s[len("//"):endPkg]
  64. s = s[endPkg:]
  65. }
  66. if !labelPkgRegexp.MatchString(pkg) {
  67. return NoLabel, fmt.Errorf("label parse error: package has invalid characters: %q", origStr)
  68. }
  69. }
  70. if s == ":" {
  71. return NoLabel, fmt.Errorf("label parse error: empty name: %q", origStr)
  72. }
  73. name := strings.TrimPrefix(s, ":")
  74. if !labelNameRegexp.MatchString(name) {
  75. return NoLabel, fmt.Errorf("label parse error: name has invalid characters: %q", origStr)
  76. }
  77. if pkg == "" && name == "" {
  78. return NoLabel, fmt.Errorf("label parse error: empty package and name: %q", origStr)
  79. }
  80. if name == "" {
  81. name = path.Base(pkg)
  82. }
  83. return Label{
  84. Repo: repo,
  85. Pkg: pkg,
  86. Name: name,
  87. Relative: relative,
  88. }, nil
  89. }
  90. func (l Label) String() string {
  91. if l.Relative {
  92. return fmt.Sprintf(":%s", l.Name)
  93. }
  94. var repo string
  95. if l.Repo != "" {
  96. repo = fmt.Sprintf("@%s", l.Repo)
  97. }
  98. if path.Base(l.Pkg) == l.Name {
  99. return fmt.Sprintf("%s//%s", repo, l.Pkg)
  100. }
  101. return fmt.Sprintf("%s//%s:%s", repo, l.Pkg, l.Name)
  102. }
  103. func (l Label) Abs(repo, pkg string) Label {
  104. if !l.Relative {
  105. return l
  106. }
  107. return Label{Repo: repo, Pkg: pkg, Name: l.Name}
  108. }
  109. func (l Label) Equal(other Label) bool {
  110. return l.Repo == other.Repo &&
  111. l.Pkg == other.Pkg &&
  112. l.Name == other.Name &&
  113. l.Relative == other.Relative
  114. }
  115. // Contains returns whether other is contained by the package of l or a
  116. // sub-package. Neither label may be relative.
  117. func (l Label) Contains(other Label) bool {
  118. if l.Relative {
  119. log.Panicf("l must not be relative: %s", l)
  120. }
  121. if other.Relative {
  122. log.Panicf("other must not be relative: %s", other)
  123. }
  124. result := l.Repo == other.Repo && pathtools.HasPrefix(other.Pkg, l.Pkg)
  125. return result
  126. }
  127. // ImportPathToBazelRepoName converts a Go import path into a bazel repo name
  128. // following the guidelines in http://bazel.io/docs/be/functions.html#workspace
  129. func ImportPathToBazelRepoName(importpath string) string {
  130. importpath = strings.ToLower(importpath)
  131. components := strings.Split(importpath, "/")
  132. labels := strings.Split(components[0], ".")
  133. var reversed []string
  134. for i := range labels {
  135. l := labels[len(labels)-i-1]
  136. reversed = append(reversed, l)
  137. }
  138. repo := strings.Join(append(reversed, components[1:]...), "_")
  139. return strings.NewReplacer("-", "_", ".", "_").Replace(repo)
  140. }