190 lines
4.3 KiB
Go
190 lines
4.3 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/fatih/camelcase"
|
|
"github.com/fatih/structtag"
|
|
)
|
|
|
|
func main() {
|
|
args := flag.Args()
|
|
if len(args) == 0 {
|
|
// Default: process the file
|
|
args = []string{os.Getenv("GOFILE")}
|
|
}
|
|
fname := args[0]
|
|
|
|
absFilePath, err := filepath.Abs(fname)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
projectRoot := os.Getenv("PROJECT_ROOT")
|
|
var paths []string
|
|
if projectRoot == "" {
|
|
// fall back to the packer root.
|
|
paths = strings.SplitAfter(absFilePath, "packer"+string(os.PathSeparator))
|
|
projectRoot = paths[0]
|
|
} else {
|
|
paths = strings.SplitAfter(absFilePath, projectRoot+string(os.PathSeparator))
|
|
}
|
|
builderName, _ := filepath.Split(paths[1])
|
|
builderName = strings.Trim(builderName, string(os.PathSeparator))
|
|
|
|
b, err := ioutil.ReadFile(fname)
|
|
if err != nil {
|
|
fmt.Printf("ReadFile: %+v", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
fset := token.NewFileSet()
|
|
f, err := parser.ParseFile(fset, fname, b, parser.ParseComments)
|
|
if err != nil {
|
|
fmt.Printf("ParseFile: %+v", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
for _, decl := range f.Decls {
|
|
typeDecl, ok := decl.(*ast.GenDecl)
|
|
if !ok {
|
|
continue
|
|
}
|
|
typeSpec, ok := typeDecl.Specs[0].(*ast.TypeSpec)
|
|
if !ok {
|
|
continue
|
|
}
|
|
structDecl, ok := typeSpec.Type.(*ast.StructType)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
fields := structDecl.Fields.List
|
|
sourcePath := filepath.ToSlash(paths[1])
|
|
header := Struct{
|
|
SourcePath: sourcePath,
|
|
Name: typeSpec.Name.Name,
|
|
Filename: typeSpec.Name.Name + ".mdx",
|
|
Header: strings.TrimSpace(typeDecl.Doc.Text()),
|
|
}
|
|
dataSourceOutput := Struct{
|
|
SourcePath: sourcePath,
|
|
Name: typeSpec.Name.Name,
|
|
Filename: typeSpec.Name.Name + ".mdx",
|
|
}
|
|
required := Struct{
|
|
SourcePath: sourcePath,
|
|
Name: typeSpec.Name.Name,
|
|
Filename: typeSpec.Name.Name + "-required.mdx",
|
|
}
|
|
notRequired := Struct{
|
|
SourcePath: sourcePath,
|
|
Name: typeSpec.Name.Name,
|
|
Filename: typeSpec.Name.Name + "-not-required.mdx",
|
|
}
|
|
|
|
for _, field := range fields {
|
|
if len(field.Names) == 0 || field.Tag == nil {
|
|
continue
|
|
}
|
|
tag := field.Tag.Value[1:]
|
|
tag = tag[:len(tag)-1]
|
|
tags, err := structtag.Parse(tag)
|
|
if err != nil {
|
|
fmt.Printf("structtag.Parse(%s): err: %v", field.Tag.Value, err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Leave undocumented tags out of markdown. This is useful for
|
|
// fields which exist for backwards compatability, or internal-use
|
|
// only fields
|
|
undocumented, _ := tags.Get("undocumented")
|
|
if undocumented != nil {
|
|
if undocumented.Name == "true" {
|
|
continue
|
|
}
|
|
}
|
|
mstr, err := tags.Get("mapstructure")
|
|
if err != nil {
|
|
continue
|
|
}
|
|
name := mstr.Name
|
|
|
|
if name == "" {
|
|
continue
|
|
}
|
|
|
|
var docs string
|
|
if field.Doc != nil {
|
|
docs = field.Doc.Text()
|
|
} else {
|
|
docs = strings.Join(camelcase.Split(field.Names[0].Name), " ")
|
|
}
|
|
|
|
if strings.Contains(docs, "TODO") {
|
|
continue
|
|
}
|
|
fieldType := string(b[field.Type.Pos()-1 : field.Type.End()-1])
|
|
fieldType = strings.ReplaceAll(fieldType, "*", `\*`)
|
|
switch fieldType {
|
|
case "time.Duration":
|
|
fieldType = `duration string | ex: "1h5m2s"`
|
|
case "config.Trilean":
|
|
fieldType = `boolean`
|
|
case "config.NameValues":
|
|
fieldType = `[]{name string, value string}`
|
|
case "config.KeyValues":
|
|
fieldType = `[]{key string, value string}`
|
|
}
|
|
|
|
field := Field{
|
|
Name: name,
|
|
Type: fieldType,
|
|
Docs: docs,
|
|
}
|
|
|
|
if typeSpec.Name.Name == "DatasourceOutput" {
|
|
dataSourceOutput.Fields = append(dataSourceOutput.Fields, field)
|
|
continue
|
|
}
|
|
|
|
if req, err := tags.Get("required"); err == nil && req.Value() == "true" {
|
|
required.Fields = append(required.Fields, field)
|
|
} else {
|
|
notRequired.Fields = append(notRequired.Fields, field)
|
|
}
|
|
}
|
|
|
|
dir := filepath.Join(projectRoot, "website", "content", "partials", builderName)
|
|
os.MkdirAll(dir, 0755)
|
|
|
|
for _, str := range []Struct{header, dataSourceOutput, required, notRequired} {
|
|
if len(str.Fields) == 0 && len(str.Header) == 0 {
|
|
continue
|
|
}
|
|
outputPath := filepath.Join(dir, str.Filename)
|
|
|
|
outputFile, err := os.Create(outputPath)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer outputFile.Close()
|
|
|
|
err = structDocsTemplate.Execute(outputFile, str)
|
|
if err != nil {
|
|
fmt.Printf("%v", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|