github.com/alex123012/deckhouse-controller-tools@v0.0.0-20230510090815-d594daf1af8c/pkg/genall/help/types.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package help 18 19 import ( 20 "sort" 21 "strings" 22 23 "sigs.k8s.io/controller-tools/pkg/markers" 24 ) 25 26 // DetailedHelp contains both a summary and further details. 27 type DetailedHelp struct { 28 // Summary contains a one-line description. 29 Summary string `json:"summary"` 30 // Details contains further information. 31 Details string `json:"details,omitempty"` 32 } 33 34 // Argument is the type data for a marker argument. 35 type Argument struct { 36 // Type is the data type of the argument (string, bool, int, slice, any, raw, invalid) 37 Type string `json:"type"` 38 // Optional marks this argument as optional. 39 Optional bool `json:"optional"` 40 // ItemType contains the type of the slice item, if this is a slice 41 ItemType *Argument `json:"itemType,omitempty"` 42 } 43 44 func (a Argument) typeString(out *strings.Builder) { 45 if a.Type == "slice" { 46 out.WriteString("[]") 47 a.ItemType.typeString(out) 48 return 49 } 50 51 out.WriteString(a.Type) 52 } 53 54 // TypeString returns a string roughly equivalent 55 // (but not identical) to the underlying Go type that 56 // this argument would parse to. It's mainly useful 57 // for user-friendly formatting of this argument (e.g. 58 // help strings). 59 func (a Argument) TypeString() string { 60 out := &strings.Builder{} 61 a.typeString(out) 62 return out.String() 63 } 64 65 // FieldHelp contains information required to print documentation for a marker field. 66 type FieldHelp struct { 67 // Name is the field name. 68 Name string `json:"name"` 69 // Argument is the type of the field. 70 Argument `json:",inline"` 71 72 // DetailedHelp contains the textual help for the field. 73 DetailedHelp `json:",inline"` 74 } 75 76 // MarkerDoc contains information required to print documentation for a marker. 77 type MarkerDoc struct { 78 // definition 79 80 // Name is the name of the marker. 81 Name string `json:"name"` 82 // Target is the target (field, package, type) of the marker. 83 Target string `json:"target"` 84 85 // help 86 87 // DetailedHelp is the textual help for the marker. 88 DetailedHelp `json:",inline"` 89 // Category is the general "category" that this marker belongs to. 90 Category string `json:"category"` 91 // DeprecatedInFavorOf marks that this marker shouldn't be used when 92 // non-nil. If also non-empty, another marker should be used instead. 93 DeprecatedInFavorOf *string `json:"deprecatedInFavorOf,omitempty"` 94 // Fields is the type and help data for each field of this marker. 95 Fields []FieldHelp `json:"fields,omitempty"` 96 } 97 98 // Empty checks if this marker has any arguments, returning true if not. 99 func (m MarkerDoc) Empty() bool { 100 return len(m.Fields) == 0 101 } 102 103 // AnonymousField chekcs if this is an single-valued marker 104 // (as opposed to having named fields). 105 func (m MarkerDoc) AnonymousField() bool { 106 return len(m.Fields) == 1 && m.Fields[0].Name == "" 107 } 108 109 // ForArgument returns the equivalent documentation for a marker argument. 110 func ForArgument(argRaw markers.Argument) Argument { 111 res := Argument{ 112 Optional: argRaw.Optional, 113 } 114 115 if argRaw.ItemType != nil { 116 itemType := ForArgument(*argRaw.ItemType) 117 res.ItemType = &itemType 118 } 119 120 switch argRaw.Type { 121 case markers.IntType: 122 res.Type = "int" 123 case markers.StringType: 124 res.Type = "string" 125 case markers.BoolType: 126 res.Type = "bool" 127 case markers.AnyType: 128 res.Type = "any" 129 case markers.SliceType: 130 res.Type = "slice" 131 case markers.RawType: 132 res.Type = "raw" 133 case markers.InvalidType: 134 res.Type = "invalid" 135 } 136 137 return res 138 } 139 140 // ForDefinition returns the equivalent marker documentation for a given marker definition and spearate help. 141 func ForDefinition(defn *markers.Definition, maybeHelp *markers.DefinitionHelp) MarkerDoc { 142 var help markers.DefinitionHelp 143 if maybeHelp != nil { 144 help = *maybeHelp 145 } 146 147 res := MarkerDoc{ 148 Name: defn.Name, 149 Category: help.Category, 150 DeprecatedInFavorOf: help.DeprecatedInFavorOf, 151 Target: defn.Target.String(), 152 DetailedHelp: DetailedHelp{Summary: help.Summary, Details: help.Details}, 153 } 154 155 helpByField := help.FieldsHelp(defn) 156 157 // TODO(directxman12): deterministic ordering 158 for fieldName, fieldHelpRaw := range helpByField { 159 fieldInfo := defn.Fields[fieldName] 160 fieldHelp := FieldHelp{ 161 Name: fieldName, 162 DetailedHelp: DetailedHelp{Summary: fieldHelpRaw.Summary, Details: fieldHelpRaw.Details}, 163 Argument: ForArgument(fieldInfo), 164 } 165 166 res.Fields = append(res.Fields, fieldHelp) 167 } 168 169 sort.Slice(res.Fields, func(i, j int) bool { return res.Fields[i].Name < res.Fields[j].Name }) 170 171 return res 172 } 173 174 // CategoryDoc contains help information for all markers in a Category. 175 type CategoryDoc struct { 176 Category string `json:"category"` 177 Markers []MarkerDoc `json:"markers"` 178 } 179 180 // ByCategory returns the marker help for markers in the given 181 // registry, grouped and sorted according to the given method. 182 func ByCategory(reg *markers.Registry, sorter SortGroup) []CategoryDoc { 183 groupedMarkers := make(map[string][]*markers.Definition) 184 185 for _, marker := range reg.AllDefinitions() { 186 group := sorter.Group(marker, reg.HelpFor(marker)) 187 groupedMarkers[group] = append(groupedMarkers[group], marker) 188 } 189 allGroups := make([]string, 0, len(groupedMarkers)) 190 for groupName := range groupedMarkers { 191 allGroups = append(allGroups, groupName) 192 } 193 194 sort.Strings(allGroups) 195 196 res := make([]CategoryDoc, len(allGroups)) 197 for i, groupName := range allGroups { 198 markers := groupedMarkers[groupName] 199 sort.Slice(markers, func(i, j int) bool { 200 return sorter.Less(markers[i], markers[j]) 201 }) 202 203 markerDocs := make([]MarkerDoc, len(markers)) 204 for i, marker := range markers { 205 markerDocs[i] = ForDefinition(marker, reg.HelpFor(marker)) 206 } 207 208 res[i] = CategoryDoc{ 209 Category: groupName, 210 Markers: markerDocs, 211 } 212 } 213 214 return res 215 }