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 }