github.com/Mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/tools/md_docs.go (about)

     1  // This file is based on the code from https://github.com/spf13/cobra
     2  // Original copyright follows
     3  //Copyright 2015 Red Hat Inc. All rights reserved.
     4  //
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  // http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package tools
    17  
    18  import (
    19  	"fmt"
    20  	"io"
    21  	"os"
    22  	"path/filepath"
    23  	"sort"
    24  	"strings"
    25  
    26  	"github.com/spf13/cobra"
    27  	"github.com/spf13/pflag"
    28  )
    29  
    30  func printOptions(out io.Writer, cmd *cobra.Command, name string) error {
    31  	if cmd.HasParent() {
    32  		return printFlagUsages(out, cmd.NonInheritedFlags(), "\n**Options**\n\n", false)
    33  	}
    34  
    35  	return printFlagUsages(out, cmd.NonInheritedFlags(), "\n## Global options\n\n", true)
    36  }
    37  
    38  func printSubcommands(out io.Writer, cmd *cobra.Command) error {
    39  	children := cmd.Commands()
    40  	sort.Sort(byName(children))
    41  
    42  	var filtered []*cobra.Command
    43  
    44  	for _, c := range children {
    45  		if c.IsAvailableCommand() && !c.IsAdditionalHelpTopicCommand() {
    46  			filtered = append(filtered, c)
    47  		}
    48  	}
    49  	if len(filtered) == 0 {
    50  		return nil
    51  	}
    52  
    53  	if _, err := fmt.Fprintf(out, "\n**Subcommands**\n\n"); err != nil {
    54  		return err
    55  	}
    56  
    57  	name := cmd.CommandPath()
    58  	for _, c := range filtered {
    59  		cname := name + " " + c.Name()
    60  		// FIXME: use better link name generation scheme
    61  		link := "#" + strings.Replace(cname, " ", "-", -1)
    62  		fmt.Fprintf(out, "* [%s](%s) - %s\n", cname, link, c.Short)
    63  
    64  	}
    65  
    66  	for _, c := range filtered {
    67  		if err := GenMarkdown(c, out); err != nil {
    68  			return err
    69  		}
    70  	}
    71  
    72  	return nil
    73  }
    74  
    75  // GenMarkdown creates markdown output.
    76  func GenMarkdown(cmd *cobra.Command, w io.Writer) error {
    77  	cmd.InitDefaultHelpCmd()
    78  	cmd.InitDefaultHelpFlag()
    79  
    80  	name := cmd.CommandPath()
    81  
    82  	short := cmd.Short
    83  	long := cmd.Long
    84  	if len(long) == 0 {
    85  		long = short
    86  	}
    87  
    88  	_, err := fmt.Fprintf(w, "## %s\n\n", name)
    89  	if err == nil {
    90  		_, err = fmt.Fprintf(w, "%s\n\n", short)
    91  	}
    92  	if err == nil {
    93  		_, err = fmt.Fprintf(w, "**Synopsis**\n\n%s\n\n", long)
    94  	}
    95  
    96  	if err == nil && cmd.Runnable() {
    97  		_, err = fmt.Fprintf(w, "```\n%s\n```\n\n", cmd.UseLine())
    98  	}
    99  
   100  	if err == nil && len(cmd.Example) > 0 {
   101  		_, err = fmt.Fprintf(w, "**Examples**\n\n```\n%s\n```\n\n", cmd.Example)
   102  	}
   103  
   104  	// we want global options to be at the end of the document
   105  	if err == nil && cmd.HasParent() {
   106  		err = printOptions(w, cmd, name)
   107  	}
   108  
   109  	if err == nil {
   110  		err = printSubcommands(w, cmd)
   111  	}
   112  
   113  	if err == nil && !cmd.HasParent() {
   114  		err = printOptions(w, cmd, name)
   115  	}
   116  
   117  	return err
   118  }
   119  
   120  // GenMarkdownTree will generate a markdown page for this command and all
   121  // descendants in the directory given. The header may be nil.
   122  // This function may not work correctly if your command names have `-` in them.
   123  // If you have `cmd` with two subcmds, `sub` and `sub-third`,
   124  // and `sub` has a subcommand called `third`, it is undefined which
   125  // help output will be in the file `cmd-sub-third.1`.
   126  func GenMarkdownTree(cmd *cobra.Command, dir string) error {
   127  	return GenMarkdownTreeCustom(cmd, dir)
   128  }
   129  
   130  // GenMarkdownTreeCustom is the the same as GenMarkdownTree, but
   131  // with custom filePrepender and linkHandler.
   132  func GenMarkdownTreeCustom(cmd *cobra.Command, dir string) error {
   133  	basename := strings.Replace(cmd.CommandPath(), " ", "_", -1) + ".md"
   134  	filename := filepath.Join(dir, basename)
   135  	f, err := os.Create(filename)
   136  	if err != nil {
   137  		return err
   138  	}
   139  	defer f.Close()
   140  
   141  	return GenMarkdown(cmd, f)
   142  }
   143  
   144  type byName []*cobra.Command
   145  
   146  func (s byName) Len() int           { return len(s) }
   147  func (s byName) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
   148  func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() }
   149  
   150  func printFlagUsages(out io.Writer, f *pflag.FlagSet, title string, includeHelpFlag bool) error {
   151  	titlePrinted := false
   152  	var err error
   153  	f.VisitAll(func(flag *pflag.Flag) {
   154  		if err != nil {
   155  			return
   156  		}
   157  
   158  		if flag.Deprecated != "" || flag.Hidden {
   159  			return
   160  		}
   161  
   162  		if !includeHelpFlag && flag.Name == "help" {
   163  			return
   164  		}
   165  
   166  		if !titlePrinted {
   167  			if _, err = out.Write([]byte(title)); err != nil {
   168  				return
   169  			}
   170  			titlePrinted = true
   171  		}
   172  
   173  		varStr := ""
   174  		varname, usage := pflag.UnquoteUsage(flag)
   175  		if varname != "" {
   176  			varStr = " " + varname
   177  		}
   178  		if flag.NoOptDefVal != "" {
   179  			switch flag.Value.Type() {
   180  			case "string":
   181  				varStr += fmt.Sprintf("[=\"%s\"]", flag.NoOptDefVal)
   182  			case "bool":
   183  				if flag.NoOptDefVal != "true" {
   184  					varStr += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
   185  				}
   186  			case "count":
   187  				if flag.NoOptDefVal != "+1" {
   188  					varStr += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
   189  				}
   190  			default:
   191  				varStr += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
   192  			}
   193  		}
   194  
   195  		if flag.Shorthand != "" && flag.ShorthandDeprecated == "" {
   196  			_, err = fmt.Fprintf(out, "\n```\n-%s, --%s%s\n```\n", flag.Shorthand, flag.Name, varStr)
   197  		} else {
   198  			_, err = fmt.Fprintf(out, "\n```\n--%s%s\n```\n", flag.Name, varStr)
   199  		}
   200  
   201  		if err == nil {
   202  			_, err = fmt.Fprintf(out, "%s\n", usage)
   203  		}
   204  
   205  		if err == nil && !defaultIsZeroValue(flag) {
   206  			if flag.Value.Type() == "string" {
   207  				_, err = fmt.Fprintf(out, " **(default value:** `%q`)\n", flag.DefValue)
   208  			} else {
   209  				_, err = fmt.Fprintf(out, " **(default value:** `%s`)\n", flag.DefValue)
   210  			}
   211  		}
   212  	})
   213  
   214  	return err
   215  }
   216  
   217  // defaultIsZeroValue returns true if the default value for this flag represents
   218  // a zero value.
   219  func defaultIsZeroValue(f *pflag.Flag) bool {
   220  	switch f.Value.Type() {
   221  	case "bool":
   222  		return f.DefValue == "false"
   223  	case "duration":
   224  		// Beginning in Go 1.7, duration zero values are "0s"
   225  		return f.DefValue == "0" || f.DefValue == "0s"
   226  	case "int", "int8", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "count", "float32", "float64":
   227  		return f.DefValue == "0"
   228  	case "string":
   229  		return f.DefValue == ""
   230  	case "ip", "ipMask", "ipNet":
   231  		return f.DefValue == "<nil>"
   232  	case "intSlice", "stringSlice", "stringArray":
   233  		return f.DefValue == "[]"
   234  	default:
   235  		switch f.Value.String() {
   236  		case "false":
   237  			return true
   238  		case "<nil>":
   239  			return true
   240  		case "":
   241  			return true
   242  		case "0":
   243  			return true
   244  		}
   245  		return false
   246  	}
   247  }