mirror of
https://github.com/siderolabs/talos.git
synced 2025-08-15 02:57:17 +02:00
Using a well known comment (docgen: nodoc), we can now tell docgen to ignore certain structs. Signed-off-by: Andrew Rynhard <andrew@andrewrynhard.com>
292 lines
5.2 KiB
Go
292 lines
5.2 KiB
Go
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"text/template"
|
|
|
|
"gopkg.in/yaml.v2"
|
|
)
|
|
|
|
var tpl = `
|
|
{{ $tick := "` + "`" + `" -}}
|
|
### {{ .Name }}
|
|
|
|
{{ range $entry := .Entries -}}
|
|
#### {{ $entry.Name }}
|
|
|
|
{{ $entry.Text.Description }}
|
|
Type: {{ $tick }}{{ $entry.Type }}{{ $tick }}
|
|
|
|
{{ if $entry.Text.Values -}}
|
|
Valid Values:
|
|
|
|
{{ range $value := $entry.Text.Values -}}
|
|
- {{ $tick }}{{ $value }}{{ $tick }}
|
|
{{ end }}
|
|
{{ end -}}
|
|
{{ if $entry.Text.Examples -}}
|
|
Examples:
|
|
{{ range $example := $entry.Text.Examples }}
|
|
{{ $tick }}{{ $tick }}{{ $tick }}yaml
|
|
{{ $example }}
|
|
{{ $tick }}{{ $tick }}{{ $tick }}
|
|
{{ end }}
|
|
{{ end }}
|
|
{{- if $entry.Note -}}
|
|
> {{ $entry.Note }}
|
|
{{ end }}
|
|
{{- end -}}
|
|
---
|
|
`
|
|
|
|
type Doc struct {
|
|
Title string
|
|
Sections []*Section
|
|
}
|
|
|
|
type Section struct {
|
|
Name string
|
|
Entries []*Entry
|
|
}
|
|
|
|
type Entry struct {
|
|
Name string
|
|
Type string
|
|
Text *Text
|
|
Note string
|
|
}
|
|
|
|
type Text struct {
|
|
Description string `json:"description"`
|
|
Values []string `json:"values"`
|
|
Examples []string `json:"examples"`
|
|
}
|
|
|
|
func in(p string) (string, error) {
|
|
return filepath.Abs(p)
|
|
}
|
|
|
|
func out(p string) (*os.File, error) {
|
|
abs, err := filepath.Abs(p)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return os.Create(abs)
|
|
}
|
|
|
|
type structType struct {
|
|
name string
|
|
pos token.Pos
|
|
node *ast.StructType
|
|
}
|
|
|
|
func collectStructs(node ast.Node) []*structType {
|
|
structs := []*structType{}
|
|
|
|
collectStructs := func(n ast.Node) bool {
|
|
g, ok := n.(*ast.GenDecl)
|
|
if !ok {
|
|
return true
|
|
}
|
|
|
|
if g.Doc != nil {
|
|
for _, comment := range g.Doc.List {
|
|
if strings.Contains(comment.Text, "docgen: nodoc") {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, spec := range g.Specs {
|
|
t, ok := spec.(*ast.TypeSpec)
|
|
if !ok {
|
|
return true
|
|
}
|
|
|
|
if t.Type == nil {
|
|
return true
|
|
}
|
|
|
|
x, ok := t.Type.(*ast.StructType)
|
|
if !ok {
|
|
return true
|
|
}
|
|
|
|
structName := t.Name.Name
|
|
|
|
s := &structType{
|
|
name: structName,
|
|
node: x,
|
|
pos: x.Pos(),
|
|
}
|
|
|
|
structs = append(structs, s)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
ast.Inspect(node, collectStructs)
|
|
|
|
return structs
|
|
}
|
|
|
|
type field struct {
|
|
}
|
|
|
|
func parseFieldType(p interface{}) string {
|
|
switch t := p.(type) {
|
|
case *ast.Ident:
|
|
return t.Name
|
|
case *ast.ArrayType:
|
|
return "array"
|
|
case *ast.MapType:
|
|
return "map"
|
|
case *ast.StructType:
|
|
return "struct"
|
|
case *ast.StarExpr:
|
|
return parseFieldType(t.X)
|
|
case *ast.SelectorExpr:
|
|
return parseFieldType(t.Sel)
|
|
default:
|
|
log.Printf("unknown: %#v", t)
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func collectFields(s *structType) (entries []*Entry) {
|
|
entries = []*Entry{}
|
|
|
|
for _, field := range s.node.Fields.List {
|
|
if field.Tag == nil {
|
|
if field.Names == nil {
|
|
// This is an embedded struct.
|
|
continue
|
|
}
|
|
log.Fatalf("field %q is missing a yaml tag", field.Names[0].Name)
|
|
}
|
|
|
|
if field.Doc == nil {
|
|
log.Fatalf("field %q is missing a documentation", field.Names[0].Name)
|
|
}
|
|
|
|
tag := reflect.StructTag(strings.Trim(field.Tag.Value, "`"))
|
|
name := tag.Get("yaml")
|
|
name = strings.Split(name, ",")[0]
|
|
|
|
fieldType := parseFieldType(field.Type)
|
|
|
|
text := &Text{}
|
|
if err := yaml.Unmarshal([]byte(field.Doc.Text()), text); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
entry := &Entry{
|
|
Name: name,
|
|
Type: fieldType,
|
|
Text: text,
|
|
}
|
|
|
|
if field.Comment != nil {
|
|
entry.Note = field.Comment.Text()
|
|
}
|
|
|
|
entries = append(entries, entry)
|
|
}
|
|
|
|
return entries
|
|
}
|
|
|
|
func render(section *Section, f *os.File) {
|
|
t := template.Must(template.New("section.tpl").Parse(tpl))
|
|
err := t.Execute(f, section)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
abs, err := in(os.Args[1])
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Printf("creating package file set: %q\n", abs)
|
|
|
|
fset := token.NewFileSet()
|
|
|
|
pkgs, err := parser.ParseDir(fset, abs, nil, parser.ParseComments)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
var structs []*structType
|
|
|
|
for _, pkg := range pkgs {
|
|
for _, astFile := range pkg.Files {
|
|
tokenFile := fset.File(astFile.Pos())
|
|
if tokenFile == nil {
|
|
continue
|
|
}
|
|
|
|
fmt.Printf("parsing file in package %q: %s\n", pkg.Name, tokenFile.Name())
|
|
structs = append(structs, collectStructs(astFile)...)
|
|
}
|
|
}
|
|
|
|
if len(structs) == 0 {
|
|
log.Fatalf("failed to find types that could be documented in %s", abs)
|
|
}
|
|
|
|
doc := &Doc{
|
|
Sections: []*Section{},
|
|
}
|
|
|
|
for _, s := range structs {
|
|
fmt.Printf("generating docs for type: %q\n", s.name)
|
|
|
|
entries := collectFields(s)
|
|
|
|
section := &Section{
|
|
Name: s.name,
|
|
Entries: entries,
|
|
}
|
|
|
|
doc.Sections = append(doc.Sections, section)
|
|
}
|
|
|
|
node, err := parser.ParseFile(fset, filepath.Join(abs, "doc.go"), nil, parser.ParseComments)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
out, err := out(os.Args[2])
|
|
defer out.Close()
|
|
|
|
out.WriteString("---\n")
|
|
out.WriteString("title: " + node.Name.Name + "\n")
|
|
out.WriteString("---\n")
|
|
out.WriteString("\n")
|
|
out.WriteString("<!-- markdownlint-disable MD024 -->")
|
|
out.WriteString("\n")
|
|
out.WriteString("\n")
|
|
out.WriteString(node.Doc.Text())
|
|
|
|
for _, section := range doc.Sections {
|
|
render(section, out)
|
|
}
|
|
}
|