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() }