github.com/nats-io/nsc@v0.0.0-20221206222106-35db9400b257/cmd/test.go (about)

     1  /*
     2   * Copyright 2018-2022 The NATS Authors
     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  
    16  package cmd
    17  
    18  import (
    19  	"bytes"
    20  	"fmt"
    21  	"path/filepath"
    22  	"sort"
    23  	"strings"
    24  
    25  	"github.com/spf13/cobra"
    26  	"github.com/spf13/cobra/doc"
    27  	flag "github.com/spf13/pflag"
    28  )
    29  
    30  // addCmd represents the add command
    31  var testCmd = &cobra.Command{
    32  	Hidden: true,
    33  	Use:    "test",
    34  	Short:  "Test commands",
    35  }
    36  
    37  func createFlagTable() *cobra.Command {
    38  	var cmds flagTable
    39  	cmd := &cobra.Command{
    40  		Use:           "flags",
    41  		Short:         "prints a table with all the flags",
    42  		SilenceErrors: true,
    43  		SilenceUsage:  true,
    44  		RunE: func(cmd *cobra.Command, args []string) error {
    45  
    46  			var buf Stack
    47  			buf.Push(rootCmd)
    48  
    49  			for {
    50  				v := buf.Pop()
    51  				if v == nil {
    52  					break
    53  				}
    54  				if v.HasSubCommands() {
    55  					for _, vv := range v.Commands() {
    56  						buf.Push(vv)
    57  					}
    58  					continue
    59  				} else {
    60  					cmds.addCmd(v)
    61  				}
    62  			}
    63  			return Write("--", []byte(cmds.render()))
    64  		},
    65  	}
    66  	return cmd
    67  }
    68  
    69  func createWhatUsesFlag() *cobra.Command {
    70  	var cmds flagTable
    71  	cmd := &cobra.Command{
    72  		Use:           "whoflag",
    73  		Short:         "prints a table with commands using the specified flag",
    74  		SilenceErrors: true,
    75  		SilenceUsage:  true,
    76  		Args:          cobra.ExactArgs(1),
    77  		RunE: func(cmd *cobra.Command, args []string) error {
    78  			var buf Stack
    79  			buf.Push(rootCmd)
    80  
    81  			for {
    82  				v := buf.Pop()
    83  				if v == nil {
    84  					break
    85  				}
    86  				if v.HasSubCommands() {
    87  					for _, vv := range v.Commands() {
    88  						buf.Push(vv)
    89  					}
    90  					continue
    91  				} else {
    92  					cmds.addCmd(v)
    93  				}
    94  			}
    95  			cmds.find(args[0])
    96  			return nil
    97  		},
    98  	}
    99  	return cmd
   100  }
   101  
   102  func generateDoc() *cobra.Command {
   103  	cmd := &cobra.Command{
   104  		Use:   "doc",
   105  		Short: "generate markdown documentation in the specified directory",
   106  		Args:  cobra.ExactArgs(1),
   107  		RunE: func(cmd *cobra.Command, args []string) error {
   108  			dir, err := filepath.Abs(args[0])
   109  			if err != nil {
   110  				return err
   111  			}
   112  			if err = MaybeMakeDir(dir); err != nil {
   113  				return err
   114  			}
   115  			return doc.GenMarkdownTree(rootCmd, dir)
   116  		},
   117  	}
   118  	return cmd
   119  }
   120  
   121  type Stack struct {
   122  	data []*cobra.Command
   123  }
   124  
   125  func (s *Stack) Push(v *cobra.Command) {
   126  	s.data = append([]*cobra.Command{v}, s.data...)
   127  }
   128  
   129  func (s *Stack) Pop() *cobra.Command {
   130  	var v *cobra.Command
   131  	if len(s.data) == 0 {
   132  		return nil
   133  	}
   134  	v = s.data[0]
   135  	s.data = s.data[1:]
   136  	return v
   137  }
   138  
   139  func reverse(a []string) {
   140  	for i := len(a)/2 - 1; i >= 0; i-- {
   141  		opp := len(a) - 1 - i
   142  		a[i], a[opp] = a[opp], a[i]
   143  	}
   144  }
   145  
   146  type parsedCommand struct {
   147  	name    string
   148  	flagMap map[string]string
   149  }
   150  
   151  type flagTable struct {
   152  	commands []parsedCommand
   153  }
   154  
   155  func (t *flagTable) addCmd(cmd *cobra.Command) {
   156  	var c parsedCommand
   157  	c.flagMap = make(map[string]string)
   158  	names := []string{cmd.Name()}
   159  	v := cmd
   160  	for {
   161  		p := v.Parent()
   162  		if p == nil {
   163  			break
   164  		}
   165  		names = append(names, p.Name())
   166  		v = p
   167  	}
   168  	reverse(names)
   169  	c.name = strings.Join(names, " ")
   170  
   171  	flags := cmd.Flags()
   172  	flags.VisitAll(func(f *flag.Flag) {
   173  		c.flagMap[f.Name] = f.Shorthand
   174  	})
   175  
   176  	t.commands = append(t.commands, c)
   177  }
   178  
   179  func (t *flagTable) find(flag string) {
   180  	for _, c := range t.commands {
   181  		m := c.flagMap
   182  		for n := range m {
   183  			if flag == m[n] || n == flag {
   184  				fmt.Println(c.name)
   185  			}
   186  		}
   187  	}
   188  }
   189  
   190  func (t *flagTable) render() string {
   191  	var buf bytes.Buffer
   192  
   193  	allFlags := make(map[string]bool)
   194  	for _, c := range t.commands {
   195  		for n := range c.flagMap {
   196  			allFlags[n] = true
   197  		}
   198  	}
   199  	var cols []string
   200  	for k := range allFlags {
   201  		cols = append(cols, k)
   202  	}
   203  	sort.Strings(cols)
   204  	cols = append([]string{"cmd"}, cols...)
   205  
   206  	buf.WriteString(strings.Join(cols, ","))
   207  	buf.WriteString("\n")
   208  
   209  	for _, c := range t.commands {
   210  		sf := []string{c.name}
   211  		for i := 1; i < len(cols); i++ {
   212  			v, ok := c.flagMap[cols[i]]
   213  			if v != "" {
   214  				sf = append(sf, v)
   215  			} else if ok {
   216  				sf = append(sf, "-")
   217  			} else {
   218  				sf = append(sf, "")
   219  			}
   220  		}
   221  		buf.WriteString(strings.Join(sf, ","))
   222  		buf.WriteString("\n")
   223  	}
   224  
   225  	return buf.String()
   226  }
   227  
   228  func init() {
   229  	GetRootCmd().AddCommand(testCmd)
   230  	testCmd.AddCommand(createGenerateNKeyCmd())
   231  	testCmd.AddCommand(createFlagTable())
   232  	testCmd.AddCommand(generateDoc())
   233  	testCmd.AddCommand(createWhatUsesFlag())
   234  }