github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/swagger_doc_generator.go (about) 1 /* 2 Copyright 2015 The Kubernetes Authors All rights reserved. 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 runtime 18 19 import ( 20 "bytes" 21 "fmt" 22 "go/ast" 23 "go/doc" 24 "go/parser" 25 "go/token" 26 "io" 27 "reflect" 28 "strings" 29 ) 30 31 // Pair of strings. We keed the name of fields and the doc 32 type Pair struct { 33 Name, Doc string 34 } 35 36 // KubeTypes is an array to represent all available types in a parsed file. [0] is for the type itself 37 type KubeTypes []Pair 38 39 func astFrom(filePath string) *doc.Package { 40 fset := token.NewFileSet() 41 m := make(map[string]*ast.File) 42 43 f, err := parser.ParseFile(fset, filePath, nil, parser.ParseComments) 44 if err != nil { 45 fmt.Println(err) 46 return nil 47 } 48 49 m[filePath] = f 50 apkg, _ := ast.NewPackage(fset, m, nil, nil) 51 52 return doc.New(apkg, "", 0) 53 } 54 55 func fmtRawDoc(rawDoc string) string { 56 var buffer bytes.Buffer 57 delPrevChar := func() { 58 if buffer.Len() > 0 { 59 buffer.Truncate(buffer.Len() - 1) // Delete the last " " or "\n" 60 } 61 } 62 63 // Ignore all lines after --- 64 rawDoc = strings.Split(rawDoc, "---")[0] 65 66 for _, line := range strings.Split(rawDoc, "\n") { 67 line = strings.TrimRight(line, " ") 68 69 if line == "" { // Keep paragraphs 70 delPrevChar() 71 buffer.WriteString("\n\n") 72 } else if !strings.HasPrefix(strings.TrimLeft(line, " "), "TODO") { // Ignore one line TODOs 73 if strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t") { 74 delPrevChar() 75 line = "\n" + line + "\n" // Replace it with newline. This is useful when we have a line with: "Example:\n\tJSON-someting..." 76 } else { 77 line += " " 78 } 79 buffer.WriteString(line) 80 } 81 } 82 83 postDoc := strings.TrimRight(buffer.String(), "\n") 84 postDoc = strings.Replace(postDoc, "\\\"", "\"", -1) // replace user's \" to " 85 postDoc = strings.Replace(postDoc, "\"", "\\\"", -1) // Escape " 86 postDoc = strings.Replace(postDoc, "\n", "\\n", -1) 87 postDoc = strings.Replace(postDoc, "\t", "\\t", -1) 88 89 return postDoc 90 } 91 92 // fieldName returns the name of the field as it should appear in JSON format 93 // "-" indicates that this field is not part of the JSON representation 94 func fieldName(field *ast.Field) string { 95 jsonTag := "" 96 if field.Tag != nil { 97 jsonTag = reflect.StructTag(field.Tag.Value[1 : len(field.Tag.Value)-1]).Get("json") // Delete first and last quotation 98 if strings.Contains(jsonTag, "inline") { 99 return "-" 100 } 101 } 102 103 jsonTag = strings.Split(jsonTag, ",")[0] // This can return "-" 104 if jsonTag == "" { 105 if field.Names != nil { 106 return field.Names[0].Name 107 } 108 return field.Type.(*ast.Ident).Name 109 } 110 return jsonTag 111 } 112 113 func writeFuncHeader(b *buffer, structName string, indent int) { 114 s := fmt.Sprintf("var map_%s = map[string]string {\n", structName) 115 b.addLine(s, indent) 116 } 117 118 func writeFuncFooter(b *buffer, structName string, indent int) { 119 b.addLine("}\n", indent) // Closes the map definition 120 121 s := fmt.Sprintf("func (%s) SwaggerDoc() map[string]string {\n", structName) 122 b.addLine(s, indent) 123 s = fmt.Sprintf("return map_%s\n", structName) 124 b.addLine(s, indent+1) 125 b.addLine("}\n", indent) // Closes the function definition 126 } 127 128 func writeMapBody(b *buffer, kubeType []Pair, indent int) { 129 format := "\"%s\": \"%s\",\n" 130 for _, pair := range kubeType { 131 s := fmt.Sprintf(format, pair.Name, pair.Doc) 132 b.addLine(s, indent+2) 133 } 134 } 135 136 // ParseDocumentationFrom gets all types' documentation and returns them as an 137 // array. Each type is again represented as an array (we have to use arrays as we 138 // need to be sure for the order of the fields). This function returns fields and 139 // struct definitions that have no documentation as {name, ""}. 140 func ParseDocumentationFrom(src string) []KubeTypes { 141 var docForTypes []KubeTypes 142 143 pkg := astFrom(src) 144 145 for _, kubType := range pkg.Types { 146 if structType, ok := kubType.Decl.Specs[0].(*ast.TypeSpec).Type.(*ast.StructType); ok { 147 var ks KubeTypes 148 ks = append(ks, Pair{kubType.Name, fmtRawDoc(kubType.Doc)}) 149 150 for _, field := range structType.Fields.List { 151 if n := fieldName(field); n != "-" { 152 fieldDoc := fmtRawDoc(field.Doc.Text()) 153 ks = append(ks, Pair{n, fieldDoc}) 154 } 155 } 156 docForTypes = append(docForTypes, ks) 157 } 158 } 159 160 return docForTypes 161 } 162 163 // WriteSwaggerDocFunc writes a declaration of a function as a string. This function is used in 164 // Swagger as a documentation source for structs and theirs fields 165 func WriteSwaggerDocFunc(kubeTypes []KubeTypes, w io.Writer) error { 166 for _, kubeType := range kubeTypes { 167 structName := kubeType[0].Name 168 kubeType[0].Name = "" 169 170 // Ignore empty documentation 171 docfulTypes := make(KubeTypes, 0, len(kubeType)) 172 for _, pair := range kubeType { 173 if pair.Doc != "" { 174 docfulTypes = append(docfulTypes, pair) 175 } 176 } 177 178 if len(docfulTypes) == 0 { 179 continue // If no fields and the struct have documentation, skip the function definition 180 } 181 182 indent := 0 183 buffer := newBuffer() 184 185 writeFuncHeader(buffer, structName, indent) 186 writeMapBody(buffer, docfulTypes, indent) 187 writeFuncFooter(buffer, structName, indent) 188 buffer.addLine("\n", 0) 189 190 if err := buffer.flushLines(w); err != nil { 191 return err 192 } 193 } 194 195 return nil 196 } 197 198 // VerifySwaggerDocsExist writes in a io.Writer a list of structs and fields that 199 // are missing of documentation. 200 func VerifySwaggerDocsExist(kubeTypes []KubeTypes, w io.Writer) (int, error) { 201 missingDocs := 0 202 buffer := newBuffer() 203 204 for _, kubeType := range kubeTypes { 205 structName := kubeType[0].Name 206 if kubeType[0].Doc == "" { 207 format := "Missing documentation for the struct itself: %s\n" 208 s := fmt.Sprintf(format, structName) 209 buffer.addLine(s, 0) 210 missingDocs++ 211 } 212 kubeType = kubeType[1:] // Skip struct definition 213 214 for _, pair := range kubeType { // Iterate only the fields 215 if pair.Doc == "" { 216 format := "In struct: %s, field documentation is missing: %s\n" 217 s := fmt.Sprintf(format, structName, pair.Name) 218 buffer.addLine(s, 0) 219 missingDocs++ 220 } 221 } 222 } 223 224 if err := buffer.flushLines(w); err != nil { 225 return -1, err 226 } 227 return missingDocs, nil 228 }