reg.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /*
  2. Copyright 2019 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package markers
  14. import (
  15. "fmt"
  16. "sync"
  17. )
  18. // Registry keeps track of registered definitions, and allows for easy lookup.
  19. // It's thread-safe, and the zero-value can be safely used.
  20. type Registry struct {
  21. forPkg map[string]*Definition
  22. forType map[string]*Definition
  23. forField map[string]*Definition
  24. helpFor map[*Definition]*DefinitionHelp
  25. mu sync.RWMutex
  26. initOnce sync.Once
  27. }
  28. func (r *Registry) init() {
  29. r.initOnce.Do(func() {
  30. if r.forPkg == nil {
  31. r.forPkg = make(map[string]*Definition)
  32. }
  33. if r.forType == nil {
  34. r.forType = make(map[string]*Definition)
  35. }
  36. if r.forField == nil {
  37. r.forField = make(map[string]*Definition)
  38. }
  39. if r.helpFor == nil {
  40. r.helpFor = make(map[*Definition]*DefinitionHelp)
  41. }
  42. })
  43. }
  44. // Define defines a new marker with the given name, target, and output type.
  45. // It's a shortcut around
  46. // r.Register(MakeDefinition(name, target, obj))
  47. func (r *Registry) Define(name string, target TargetType, obj interface{}) error {
  48. def, err := MakeDefinition(name, target, obj)
  49. if err != nil {
  50. return err
  51. }
  52. return r.Register(def)
  53. }
  54. // Register registers the given marker definition with this registry for later lookup.
  55. func (r *Registry) Register(def *Definition) error {
  56. r.init()
  57. r.mu.Lock()
  58. defer r.mu.Unlock()
  59. switch def.Target {
  60. case DescribesPackage:
  61. r.forPkg[def.Name] = def
  62. case DescribesType:
  63. r.forType[def.Name] = def
  64. case DescribesField:
  65. r.forField[def.Name] = def
  66. default:
  67. return fmt.Errorf("unknown target type %v", def.Target)
  68. }
  69. return nil
  70. }
  71. // AddHelp stores the given help in the registry, marking it as associated with
  72. // the given definition.
  73. func (r *Registry) AddHelp(def *Definition, help *DefinitionHelp) {
  74. r.init()
  75. r.mu.Lock()
  76. defer r.mu.Unlock()
  77. r.helpFor[def] = help
  78. }
  79. // Lookup fetches the definition corresponding to the given name and target type.
  80. func (r *Registry) Lookup(name string, target TargetType) *Definition {
  81. r.init()
  82. r.mu.RLock()
  83. defer r.mu.RUnlock()
  84. switch target {
  85. case DescribesPackage:
  86. return tryAnonLookup(name, r.forPkg)
  87. case DescribesType:
  88. return tryAnonLookup(name, r.forType)
  89. case DescribesField:
  90. return tryAnonLookup(name, r.forField)
  91. default:
  92. return nil
  93. }
  94. }
  95. // HelpFor fetches the help for a given definition, if present.
  96. func (r *Registry) HelpFor(def *Definition) *DefinitionHelp {
  97. r.init()
  98. r.mu.RLock()
  99. defer r.mu.RUnlock()
  100. return r.helpFor[def]
  101. }
  102. // AllDefinitions returns all marker definitions known to this registry.
  103. func (r *Registry) AllDefinitions() []*Definition {
  104. res := make([]*Definition, 0, len(r.forPkg)+len(r.forType)+len(r.forField))
  105. for _, def := range r.forPkg {
  106. res = append(res, def)
  107. }
  108. for _, def := range r.forType {
  109. res = append(res, def)
  110. }
  111. for _, def := range r.forField {
  112. res = append(res, def)
  113. }
  114. return res
  115. }
  116. // tryAnonLookup tries looking up the given marker as both an struct-based
  117. // marker and an anonymous marker, returning whichever format matches first,
  118. // preferring the longer (anonymous) name in case of conflicts.
  119. func tryAnonLookup(name string, defs map[string]*Definition) *Definition {
  120. // NB(directxman12): we look up anonymous names first to work with
  121. // legacy style marker definitions that have a namespaced approach
  122. // (e.g. deepcopy-gen, which uses `+k8s:deepcopy-gen=foo,bar` *and*
  123. // `+k8s.io:deepcopy-gen:interfaces=foo`).
  124. name, anonName, _ := splitMarker(name)
  125. if def, exists := defs[anonName]; exists {
  126. return def
  127. }
  128. return defs[name]
  129. }