github.com/TheSpiritXIII/controller-tools@v0.14.1/pkg/markers/zip.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 markers 18 19 import ( 20 "go/ast" 21 "go/token" 22 "reflect" 23 "strings" 24 25 "github.com/TheSpiritXIII/controller-tools/pkg/loader" 26 ) 27 28 // extractDoc extracts documentation from the given node, skipping markers 29 // in the godoc and falling back to the decl if necessary (for single-line decls). 30 func extractDoc(node ast.Node, decl *ast.GenDecl) string { 31 var docs *ast.CommentGroup 32 switch docced := node.(type) { 33 case *ast.Field: 34 docs = docced.Doc 35 case *ast.File: 36 docs = docced.Doc 37 case *ast.GenDecl: 38 docs = docced.Doc 39 case *ast.TypeSpec: 40 docs = docced.Doc 41 // type Ident expr expressions get docs attached to the decl, 42 // so check for that case (missing Lparen == single line type decl) 43 if docs == nil && decl.Lparen == token.NoPos { 44 docs = decl.Doc 45 } 46 } 47 48 if docs == nil { 49 return "" 50 } 51 52 // filter out markers 53 var outGroup ast.CommentGroup 54 outGroup.List = make([]*ast.Comment, 0, len(docs.List)) 55 for _, comment := range docs.List { 56 if isMarkerComment(comment.Text) { 57 continue 58 } 59 outGroup.List = append(outGroup.List, comment) 60 } 61 isAsteriskComment := false 62 for _, l := range outGroup.List { 63 if strings.HasPrefix(l.Text, "/*") { 64 isAsteriskComment = true 65 break 66 } 67 } 68 69 // split lines, and re-join together as a single 70 // paragraph, respecting double-newlines as 71 // paragraph markers. 72 outLines := strings.Split(outGroup.Text(), "\n") 73 if outLines[len(outLines)-1] == "" { 74 // chop off the extraneous last part 75 outLines = outLines[:len(outLines)-1] 76 } 77 78 for i, line := range outLines { 79 if isAsteriskComment { 80 // Trim any extranous whitespace, 81 // for handling /*…*/-style comments, 82 // which have whitespace preserved in go/ast: 83 line = strings.TrimSpace(line) 84 } 85 86 // Respect that double-newline means 87 // actual newline: 88 if line == "" { 89 outLines[i] = "\n" 90 } else { 91 outLines[i] = line 92 } 93 } 94 return strings.Join(outLines, "\n") 95 } 96 97 // PackageMarkers collects all the package-level marker values for the given package. 98 func PackageMarkers(col *Collector, pkg *loader.Package) (MarkerValues, error) { 99 markers, err := col.MarkersInPackage(pkg) 100 if err != nil { 101 return nil, err 102 } 103 res := make(MarkerValues) 104 for _, file := range pkg.Syntax { 105 fileMarkers := markers[file] 106 for name, vals := range fileMarkers { 107 res[name] = append(res[name], vals...) 108 } 109 } 110 111 return res, nil 112 } 113 114 // FieldInfo contains marker values and commonly used information for a struct field. 115 type FieldInfo struct { 116 // Name is the name of the field (or "" for embedded fields) 117 Name string 118 // Doc is the Godoc of the field, pre-processed to remove markers and joine 119 // single newlines together. 120 Doc string 121 // Tag struct tag associated with this field (or "" if non existed). 122 Tag reflect.StructTag 123 124 // Markers are all registered markers associated with this field. 125 Markers MarkerValues 126 127 // RawField is the raw, underlying field AST object that this field represents. 128 RawField *ast.Field 129 } 130 131 // TypeInfo contains marker values and commonly used information for a type declaration. 132 type TypeInfo struct { 133 // Name is the name of the type. 134 Name string 135 // Doc is the Godoc of the type, pre-processed to remove markers and joine 136 // single newlines together. 137 Doc string 138 139 // Markers are all registered markers associated with the type. 140 Markers MarkerValues 141 142 // Fields are all the fields associated with the type, if it's a struct. 143 // (if not, Fields will be nil). 144 Fields []FieldInfo 145 146 // RawDecl contains the raw GenDecl that the type was declared as part of. 147 RawDecl *ast.GenDecl 148 // RawSpec contains the raw Spec that declared this type. 149 RawSpec *ast.TypeSpec 150 // RawFile contains the file in which this type was declared. 151 RawFile *ast.File 152 } 153 154 // TypeCallback is a callback called for each type declaration in a package. 155 type TypeCallback func(info *TypeInfo) 156 157 // EachType collects all markers, then calls the given callback for each type declaration in a package. 158 // Each individual spec is considered separate, so 159 // 160 // type ( 161 // Foo string 162 // Bar int 163 // Baz struct{} 164 // ) 165 // 166 // yields three calls to the callback. 167 func EachType(col *Collector, pkg *loader.Package, cb TypeCallback) error { 168 markers, err := col.MarkersInPackage(pkg) 169 if err != nil { 170 return err 171 } 172 173 loader.EachType(pkg, func(file *ast.File, decl *ast.GenDecl, spec *ast.TypeSpec) { 174 var fields []FieldInfo 175 if structSpec, isStruct := spec.Type.(*ast.StructType); isStruct { 176 for _, field := range structSpec.Fields.List { 177 for _, name := range field.Names { 178 fields = append(fields, FieldInfo{ 179 Name: name.Name, 180 Doc: extractDoc(field, nil), 181 Tag: loader.ParseAstTag(field.Tag), 182 Markers: markers[field], 183 RawField: field, 184 }) 185 } 186 if field.Names == nil { 187 fields = append(fields, FieldInfo{ 188 Doc: extractDoc(field, nil), 189 Tag: loader.ParseAstTag(field.Tag), 190 Markers: markers[field], 191 RawField: field, 192 }) 193 } 194 } 195 } 196 197 cb(&TypeInfo{ 198 Name: spec.Name.Name, 199 Markers: markers[spec], 200 Doc: extractDoc(spec, decl), 201 Fields: fields, 202 RawDecl: decl, 203 RawSpec: spec, 204 RawFile: file, 205 }) 206 }) 207 208 return nil 209 }