github.com/yogeshlonkar/moby@v1.13.2-0.20201203103638-c0b64beaea94/docs/yaml/yaml.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "path/filepath" 8 "sort" 9 "strings" 10 11 "github.com/spf13/cobra" 12 "github.com/spf13/pflag" 13 "gopkg.in/yaml.v2" 14 ) 15 16 type cmdOption struct { 17 Option string 18 Shorthand string `yaml:",omitempty"` 19 DefaultValue string `yaml:"default_value,omitempty"` 20 Description string `yaml:",omitempty"` 21 } 22 23 type cmdDoc struct { 24 Name string `yaml:"command"` 25 SeeAlso []string `yaml:"parent,omitempty"` 26 Version string `yaml:"engine_version,omitempty"` 27 Aliases string `yaml:",omitempty"` 28 Short string `yaml:",omitempty"` 29 Long string `yaml:",omitempty"` 30 Usage string `yaml:",omitempty"` 31 Pname string `yaml:",omitempty"` 32 Plink string `yaml:",omitempty"` 33 Cname []string `yaml:",omitempty"` 34 Clink []string `yaml:",omitempty"` 35 Options []cmdOption `yaml:",omitempty"` 36 InheritedOptions []cmdOption `yaml:"inherited_options,omitempty"` 37 Example string `yaml:"examples,omitempty"` 38 } 39 40 // GenYamlTree creates yaml structured ref files 41 func GenYamlTree(cmd *cobra.Command, dir string) error { 42 identity := func(s string) string { return s } 43 emptyStr := func(s string) string { return "" } 44 return GenYamlTreeCustom(cmd, dir, emptyStr, identity) 45 } 46 47 // GenYamlTreeCustom creates yaml structured ref files 48 func GenYamlTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHandler func(string) string) error { 49 for _, c := range cmd.Commands() { 50 if !c.IsAvailableCommand() || c.IsHelpCommand() { 51 continue 52 } 53 if err := GenYamlTreeCustom(c, dir, filePrepender, linkHandler); err != nil { 54 return err 55 } 56 } 57 58 basename := strings.Replace(cmd.CommandPath(), " ", "_", -1) + ".yaml" 59 filename := filepath.Join(dir, basename) 60 f, err := os.Create(filename) 61 if err != nil { 62 return err 63 } 64 defer f.Close() 65 66 if _, err := io.WriteString(f, filePrepender(filename)); err != nil { 67 return err 68 } 69 if err := GenYamlCustom(cmd, f, linkHandler); err != nil { 70 return err 71 } 72 return nil 73 } 74 75 // GenYamlCustom creates custom yaml output 76 func GenYamlCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string) string) error { 77 cliDoc := cmdDoc{} 78 cliDoc.Name = cmd.CommandPath() 79 80 // Check experimental: ok := cmd.Tags["experimental"] 81 82 cliDoc.Aliases = strings.Join(cmd.Aliases, ", ") 83 cliDoc.Short = cmd.Short 84 cliDoc.Long = cmd.Long 85 if len(cliDoc.Long) == 0 { 86 cliDoc.Long = cliDoc.Short 87 } 88 89 if cmd.Runnable() { 90 cliDoc.Usage = cmd.UseLine() 91 } 92 93 if len(cmd.Example) > 0 { 94 cliDoc.Example = cmd.Example 95 } 96 97 flags := cmd.NonInheritedFlags() 98 if flags.HasFlags() { 99 cliDoc.Options = genFlagResult(flags) 100 } 101 flags = cmd.InheritedFlags() 102 if flags.HasFlags() { 103 cliDoc.InheritedOptions = genFlagResult(flags) 104 } 105 106 if hasSeeAlso(cmd) { 107 if cmd.HasParent() { 108 parent := cmd.Parent() 109 cliDoc.Pname = parent.CommandPath() 110 link := cliDoc.Pname + ".yaml" 111 cliDoc.Plink = strings.Replace(link, " ", "_", -1) 112 cmd.VisitParents(func(c *cobra.Command) { 113 if c.DisableAutoGenTag { 114 cmd.DisableAutoGenTag = c.DisableAutoGenTag 115 } 116 }) 117 } 118 119 children := cmd.Commands() 120 sort.Sort(byName(children)) 121 122 for _, child := range children { 123 if !child.IsAvailableCommand() || child.IsHelpCommand() { 124 continue 125 } 126 currentChild := cliDoc.Name + " " + child.Name() 127 cliDoc.Cname = append(cliDoc.Cname, cliDoc.Name+" "+child.Name()) 128 link := currentChild + ".yaml" 129 cliDoc.Clink = append(cliDoc.Clink, strings.Replace(link, " ", "_", -1)) 130 } 131 } 132 133 final, err := yaml.Marshal(&cliDoc) 134 if err != nil { 135 fmt.Println(err) 136 os.Exit(1) 137 } 138 if _, err := fmt.Fprintln(w, string(final)); err != nil { 139 return err 140 } 141 return nil 142 } 143 144 func genFlagResult(flags *pflag.FlagSet) []cmdOption { 145 var result []cmdOption 146 147 flags.VisitAll(func(flag *pflag.Flag) { 148 // Todo, when we mark a shorthand is deprecated, but specify an empty message. 149 // The flag.ShorthandDeprecated is empty as the shorthand is deprecated. 150 // Using len(flag.ShorthandDeprecated) > 0 can't handle this, others are ok. 151 if !(len(flag.ShorthandDeprecated) > 0) && len(flag.Shorthand) > 0 { 152 opt := cmdOption{ 153 Option: flag.Name, 154 Shorthand: flag.Shorthand, 155 DefaultValue: flag.DefValue, 156 Description: forceMultiLine(flag.Usage), 157 } 158 result = append(result, opt) 159 } else { 160 opt := cmdOption{ 161 Option: flag.Name, 162 DefaultValue: forceMultiLine(flag.DefValue), 163 Description: forceMultiLine(flag.Usage), 164 } 165 result = append(result, opt) 166 } 167 }) 168 169 return result 170 } 171 172 // Temporary workaround for yaml lib generating incorrect yaml with long strings 173 // that do not contain \n. 174 func forceMultiLine(s string) string { 175 if len(s) > 60 && !strings.Contains(s, "\n") { 176 s = s + "\n" 177 } 178 return s 179 } 180 181 // Small duplication for cobra utils 182 func hasSeeAlso(cmd *cobra.Command) bool { 183 if cmd.HasParent() { 184 return true 185 } 186 for _, c := range cmd.Commands() { 187 if !c.IsAvailableCommand() || c.IsHelpCommand() { 188 continue 189 } 190 return true 191 } 192 return false 193 } 194 195 func parseMDContent(mdString string) (description string, examples string) { 196 parsedContent := strings.Split(mdString, "\n## ") 197 for _, s := range parsedContent { 198 if strings.Index(s, "Description") == 0 { 199 description = strings.Trim(s, "Description\n") 200 } 201 if strings.Index(s, "Examples") == 0 { 202 examples = strings.Trim(s, "Examples\n") 203 } 204 } 205 return 206 } 207 208 type byName []*cobra.Command 209 210 func (s byName) Len() int { return len(s) } 211 func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 212 func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() }