Files
kustomize/cmd/mdtogo/main.go
2022-08-10 18:22:46 -04:00

227 lines
4.9 KiB
Go

// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package main generates cobra.Command go variables containing documentation read from .md files.
// Usage: mdtogo SOURCE_MD_DIR/ DEST_GO_DIR/ [--full=true] [--license=license.txt|none]
//
// The command will create a docs.go file under DEST_GO_DIR/ containing string variables to be
// used by cobra commands for documentation.The variable names are generated from the SOURCE_MD_DIR/
// file names, replacing '-' with '', title casing the filename, and dropping the extension.
// All *.md will be read from DEST_GO_DIR/, and a single DEST_GO_DIR/docs.go file is generated.
//
// Each .md document will be parsed as follows if no flags are provided:
//
// ## cmd
//
// This section will be parsed into a string variable for `Short`
//
// ### Synopsis
//
// This section will be parsed into a string variable for `Long`
//
// ### Examples
//
// This section will be parsed into a string variable for `Example`
//
// If --full=true is provided, the document will be parsed as follows:
//
// ## cmd
//
// All sections will be parsed into a Long string.
//
// Flags:
// --full=true
// Create a Long variable from the full .md files, rather than separate sections.
// --license
// Controls the license header added to the files. Specify a path to a license file,
// or "none" to skip adding a license.
package main
import (
"bufio"
"bytes"
"fmt"
"log"
"os"
"path/filepath"
"strings"
)
var full bool
var licenseFile string
func main() {
for _, a := range os.Args {
if a == "--full=true" {
full = true
}
if strings.HasPrefix(a, "--license=") {
licenseFile = strings.ReplaceAll(a, "--license=", "")
}
}
if len(os.Args) < 3 {
fmt.Fprintf(os.Stderr, "Usage: mdtogo SOURCE_MD_DIR/ DEST_GO_DIR/\n")
os.Exit(1)
}
source := os.Args[1]
dest := os.Args[2]
files, err := os.ReadDir(source)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
var docs []doc
for _, f := range files {
if filepath.Ext(f.Name()) != ".md" {
continue
}
b, err := os.ReadFile(filepath.Join(source, f.Name()))
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
docs = append(docs, parse(f.Name(), string(b)))
}
var license string
if licenseFile == "" {
license = `// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0`
} else if licenseFile == "none" {
// no license -- maybe added by another tool
} else {
b, err := os.ReadFile(licenseFile)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
license = string(b)
}
out := []string{license, `
// Code generated by "mdtogo"; DO NOT EDIT.
package ` + filepath.Base(dest) + "\n"}
for i := range docs {
out = append(out, docs[i].String())
}
if _, err := os.Stat(dest); err != nil {
_ = os.Mkdir(dest, 0700)
}
o := strings.Join(out, "\n")
err = os.WriteFile(filepath.Join(dest, "docs.go"), []byte(o), 0600)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
}
func parse(name, value string) doc {
name = strings.ReplaceAll(name, filepath.Ext(name), "")
name = strings.Title(name)
name = strings.ReplaceAll(name, "-", "")
scanner := bufio.NewScanner(bytes.NewBufferString(value))
var long, examples []string
var short string
var isLong, isExample, isIndent bool
var doc doc
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "## ") && short == "" {
for scanner.Scan() {
if strings.TrimSpace(scanner.Text()) == "" {
continue
}
short = scanner.Text()
break
}
continue
}
if !full {
if strings.HasPrefix(line, "### Synopsis") {
isLong = true
isExample = false
continue
}
if strings.HasPrefix(line, "### Examples") {
isLong = false
isExample = true
continue
}
if strings.HasPrefix(line, "### ") {
isLong = false
isExample = false
continue
}
}
if strings.HasPrefix(line, "```") {
isIndent = !isIndent
continue
}
line = strings.ReplaceAll(line, "`", "` + \"`\" + `")
if isIndent {
line = "\t" + line
}
if isLong || full {
long = append(long, line)
continue
}
if isExample {
examples = append(examples, line)
}
}
doc.Name = name
doc.Short = short
doc.Long = strings.Join(long, "\n")
doc.Examples = strings.Join(examples, "\n")
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
return doc
}
type doc struct {
Name string
Short string
Long string
Examples string
}
func (d doc) String() string {
var parts []string
if d.Short != "" {
parts = append(parts,
fmt.Sprintf("var %sShort=`%s`", d.Name, d.Short))
}
if d.Long != "" {
parts = append(parts,
fmt.Sprintf("var %sLong=`%s`", d.Name, d.Long))
}
if d.Examples != "" {
parts = append(parts,
fmt.Sprintf("var %sExamples=`%s`", d.Name, d.Examples))
}
return strings.Join(parts, "\n") + "\n"
}