...

Source file src/github.com/spf13/cobra/doc/yaml_docs.go

Documentation: github.com/spf13/cobra/doc

     1  // Copyright 2013-2023 The Cobra Authors
     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  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package doc
    16  
    17  import (
    18  	"fmt"
    19  	"io"
    20  	"os"
    21  	"path/filepath"
    22  	"sort"
    23  	"strings"
    24  
    25  	"github.com/spf13/cobra"
    26  	"github.com/spf13/pflag"
    27  	"gopkg.in/yaml.v3"
    28  )
    29  
    30  type cmdOption struct {
    31  	Name         string
    32  	Shorthand    string `yaml:",omitempty"`
    33  	DefaultValue string `yaml:"default_value,omitempty"`
    34  	Usage        string `yaml:",omitempty"`
    35  }
    36  
    37  type cmdDoc struct {
    38  	Name             string
    39  	Synopsis         string      `yaml:",omitempty"`
    40  	Description      string      `yaml:",omitempty"`
    41  	Usage            string      `yaml:",omitempty"`
    42  	Options          []cmdOption `yaml:",omitempty"`
    43  	InheritedOptions []cmdOption `yaml:"inherited_options,omitempty"`
    44  	Example          string      `yaml:",omitempty"`
    45  	SeeAlso          []string    `yaml:"see_also,omitempty"`
    46  }
    47  
    48  // GenYamlTree creates yaml structured ref files for this command and all descendants
    49  // in the directory given. This function may not work
    50  // correctly if your command names have `-` in them. If you have `cmd` with two
    51  // subcmds, `sub` and `sub-third`, and `sub` has a subcommand called `third`
    52  // it is undefined which help output will be in the file `cmd-sub-third.1`.
    53  func GenYamlTree(cmd *cobra.Command, dir string) error {
    54  	identity := func(s string) string { return s }
    55  	emptyStr := func(s string) string { return "" }
    56  	return GenYamlTreeCustom(cmd, dir, emptyStr, identity)
    57  }
    58  
    59  // GenYamlTreeCustom creates yaml structured ref files.
    60  func GenYamlTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHandler func(string) string) error {
    61  	for _, c := range cmd.Commands() {
    62  		if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
    63  			continue
    64  		}
    65  		if err := GenYamlTreeCustom(c, dir, filePrepender, linkHandler); err != nil {
    66  			return err
    67  		}
    68  	}
    69  
    70  	basename := strings.ReplaceAll(cmd.CommandPath(), " ", "_") + ".yaml"
    71  	filename := filepath.Join(dir, basename)
    72  	f, err := os.Create(filename)
    73  	if err != nil {
    74  		return err
    75  	}
    76  	defer f.Close()
    77  
    78  	if _, err := io.WriteString(f, filePrepender(filename)); err != nil {
    79  		return err
    80  	}
    81  	if err := GenYamlCustom(cmd, f, linkHandler); err != nil {
    82  		return err
    83  	}
    84  	return nil
    85  }
    86  
    87  // GenYaml creates yaml output.
    88  func GenYaml(cmd *cobra.Command, w io.Writer) error {
    89  	return GenYamlCustom(cmd, w, func(s string) string { return s })
    90  }
    91  
    92  // GenYamlCustom creates custom yaml output.
    93  func GenYamlCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string) string) error {
    94  	cmd.InitDefaultHelpCmd()
    95  	cmd.InitDefaultHelpFlag()
    96  
    97  	yamlDoc := cmdDoc{}
    98  	yamlDoc.Name = cmd.CommandPath()
    99  
   100  	yamlDoc.Synopsis = forceMultiLine(cmd.Short)
   101  	yamlDoc.Description = forceMultiLine(cmd.Long)
   102  
   103  	if cmd.Runnable() {
   104  		yamlDoc.Usage = cmd.UseLine()
   105  	}
   106  
   107  	if len(cmd.Example) > 0 {
   108  		yamlDoc.Example = cmd.Example
   109  	}
   110  
   111  	flags := cmd.NonInheritedFlags()
   112  	if flags.HasFlags() {
   113  		yamlDoc.Options = genFlagResult(flags)
   114  	}
   115  	flags = cmd.InheritedFlags()
   116  	if flags.HasFlags() {
   117  		yamlDoc.InheritedOptions = genFlagResult(flags)
   118  	}
   119  
   120  	if hasSeeAlso(cmd) {
   121  		result := []string{}
   122  		if cmd.HasParent() {
   123  			parent := cmd.Parent()
   124  			result = append(result, parent.CommandPath()+" - "+parent.Short)
   125  		}
   126  		children := cmd.Commands()
   127  		sort.Sort(byName(children))
   128  		for _, child := range children {
   129  			if !child.IsAvailableCommand() || child.IsAdditionalHelpTopicCommand() {
   130  				continue
   131  			}
   132  			result = append(result, child.CommandPath()+" - "+child.Short)
   133  		}
   134  		yamlDoc.SeeAlso = result
   135  	}
   136  
   137  	final, err := yaml.Marshal(&yamlDoc)
   138  	if err != nil {
   139  		fmt.Println(err)
   140  		os.Exit(1)
   141  	}
   142  
   143  	if _, err := w.Write(final); err != nil {
   144  		return err
   145  	}
   146  	return nil
   147  }
   148  
   149  func genFlagResult(flags *pflag.FlagSet) []cmdOption {
   150  	var result []cmdOption
   151  
   152  	flags.VisitAll(func(flag *pflag.Flag) {
   153  		// Todo, when we mark a shorthand is deprecated, but specify an empty message.
   154  		// The flag.ShorthandDeprecated is empty as the shorthand is deprecated.
   155  		// Using len(flag.ShorthandDeprecated) > 0 can't handle this, others are ok.
   156  		if !(len(flag.ShorthandDeprecated) > 0) && len(flag.Shorthand) > 0 {
   157  			opt := cmdOption{
   158  				flag.Name,
   159  				flag.Shorthand,
   160  				flag.DefValue,
   161  				forceMultiLine(flag.Usage),
   162  			}
   163  			result = append(result, opt)
   164  		} else {
   165  			opt := cmdOption{
   166  				Name:         flag.Name,
   167  				DefaultValue: forceMultiLine(flag.DefValue),
   168  				Usage:        forceMultiLine(flag.Usage),
   169  			}
   170  			result = append(result, opt)
   171  		}
   172  	})
   173  
   174  	return result
   175  }
   176  

View as plain text