swagger_doc_generator.go 7.0 KB

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