rest_docs.go

  1//Copyright 2015 Red Hat Inc. All rights reserved.
  2//
  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//
  8// Unless required by applicable law or agreed to in writing, software
  9// distributed under the License is distributed on an "AS IS" BASIS,
 10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 11// See the License for the specific language governing permissions and
 12// limitations under the License.
 13
 14package doc
 15
 16import (
 17	"bytes"
 18	"fmt"
 19	"io"
 20	"os"
 21	"path/filepath"
 22	"sort"
 23	"strings"
 24	"time"
 25
 26	"github.com/spf13/cobra"
 27)
 28
 29func printOptionsReST(buf *bytes.Buffer, cmd *cobra.Command, name string) error {
 30	flags := cmd.NonInheritedFlags()
 31	flags.SetOutput(buf)
 32	if flags.HasAvailableFlags() {
 33		buf.WriteString("Options\n")
 34		buf.WriteString("~~~~~~~\n\n::\n\n")
 35		flags.PrintDefaults()
 36		buf.WriteString("\n")
 37	}
 38
 39	parentFlags := cmd.InheritedFlags()
 40	parentFlags.SetOutput(buf)
 41	if parentFlags.HasAvailableFlags() {
 42		buf.WriteString("Options inherited from parent commands\n")
 43		buf.WriteString("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n::\n\n")
 44		parentFlags.PrintDefaults()
 45		buf.WriteString("\n")
 46	}
 47	return nil
 48}
 49
 50// linkHandler for default ReST hyperlink markup
 51func defaultLinkHandler(name, ref string) string {
 52	return fmt.Sprintf("`%s <%s.rst>`_", name, ref)
 53}
 54
 55// GenReST creates reStructured Text output.
 56func GenReST(cmd *cobra.Command, w io.Writer) error {
 57	return GenReSTCustom(cmd, w, defaultLinkHandler)
 58}
 59
 60// GenReSTCustom creates custom reStructured Text output.
 61func GenReSTCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string, string) string) error {
 62	cmd.InitDefaultHelpCmd()
 63	cmd.InitDefaultHelpFlag()
 64
 65	buf := new(bytes.Buffer)
 66	name := cmd.CommandPath()
 67
 68	short := cmd.Short
 69	long := cmd.Long
 70	if len(long) == 0 {
 71		long = short
 72	}
 73	ref := strings.Replace(name, " ", "_", -1)
 74
 75	buf.WriteString(".. _" + ref + ":\n\n")
 76	buf.WriteString(name + "\n")
 77	buf.WriteString(strings.Repeat("-", len(name)) + "\n\n")
 78	buf.WriteString(short + "\n\n")
 79	buf.WriteString("Synopsis\n")
 80	buf.WriteString("~~~~~~~~\n\n")
 81	buf.WriteString("\n" + long + "\n\n")
 82
 83	if cmd.Runnable() {
 84		buf.WriteString(fmt.Sprintf("::\n\n  %s\n\n", cmd.UseLine()))
 85	}
 86
 87	if len(cmd.Example) > 0 {
 88		buf.WriteString("Examples\n")
 89		buf.WriteString("~~~~~~~~\n\n")
 90		buf.WriteString(fmt.Sprintf("::\n\n%s\n\n", indentString(cmd.Example, "  ")))
 91	}
 92
 93	if err := printOptionsReST(buf, cmd, name); err != nil {
 94		return err
 95	}
 96	if hasSeeAlso(cmd) {
 97		buf.WriteString("SEE ALSO\n")
 98		buf.WriteString("~~~~~~~~\n\n")
 99		if cmd.HasParent() {
100			parent := cmd.Parent()
101			pname := parent.CommandPath()
102			ref = strings.Replace(pname, " ", "_", -1)
103			buf.WriteString(fmt.Sprintf("* %s \t - %s\n", linkHandler(pname, ref), parent.Short))
104			cmd.VisitParents(func(c *cobra.Command) {
105				if c.DisableAutoGenTag {
106					cmd.DisableAutoGenTag = c.DisableAutoGenTag
107				}
108			})
109		}
110
111		children := cmd.Commands()
112		sort.Sort(byName(children))
113
114		for _, child := range children {
115			if !child.IsAvailableCommand() || child.IsAdditionalHelpTopicCommand() {
116				continue
117			}
118			cname := name + " " + child.Name()
119			ref = strings.Replace(cname, " ", "_", -1)
120			buf.WriteString(fmt.Sprintf("* %s \t - %s\n", linkHandler(cname, ref), child.Short))
121		}
122		buf.WriteString("\n")
123	}
124	if !cmd.DisableAutoGenTag {
125		buf.WriteString("*Auto generated by spf13/cobra on " + time.Now().Format("2-Jan-2006") + "*\n")
126	}
127	_, err := buf.WriteTo(w)
128	return err
129}
130
131// GenReSTTree will generate a ReST page for this command and all
132// descendants in the directory given.
133// This function may not work correctly if your command names have `-` in them.
134// If you have `cmd` with two subcmds, `sub` and `sub-third`,
135// and `sub` has a subcommand called `third`, it is undefined which
136// help output will be in the file `cmd-sub-third.1`.
137func GenReSTTree(cmd *cobra.Command, dir string) error {
138	emptyStr := func(s string) string { return "" }
139	return GenReSTTreeCustom(cmd, dir, emptyStr, defaultLinkHandler)
140}
141
142// GenReSTTreeCustom is the the same as GenReSTTree, but
143// with custom filePrepender and linkHandler.
144func GenReSTTreeCustom(cmd *cobra.Command, dir string, filePrepender func(string) string, linkHandler func(string, string) string) error {
145	for _, c := range cmd.Commands() {
146		if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
147			continue
148		}
149		if err := GenReSTTreeCustom(c, dir, filePrepender, linkHandler); err != nil {
150			return err
151		}
152	}
153
154	basename := strings.Replace(cmd.CommandPath(), " ", "_", -1) + ".rst"
155	filename := filepath.Join(dir, basename)
156	f, err := os.Create(filename)
157	if err != nil {
158		return err
159	}
160	defer f.Close()
161
162	if _, err := io.WriteString(f, filePrepender(filename)); err != nil {
163		return err
164	}
165	if err := GenReSTCustom(cmd, f, linkHandler); err != nil {
166		return err
167	}
168	return nil
169}
170
171// adapted from: https://github.com/kr/text/blob/main/indent.go
172func indentString(s, p string) string {
173	var res []byte
174	b := []byte(s)
175	prefix := []byte(p)
176	bol := true
177	for _, c := range b {
178		if bol && c != '\n' {
179			res = append(res, prefix...)
180		}
181		res = append(res, c)
182		bol = c == '\n'
183	}
184	return string(res)
185}