github.com/wanddynosios/cli/v8@v8.7.9-0.20240221182337-1a92e3a7017f/actor/sharedaction/help.go (about)

     1  package sharedaction
     2  
     3  import (
     4  	"reflect"
     5  	"sort"
     6  	"strings"
     7  
     8  	"code.cloudfoundry.org/cli/actor/actionerror"
     9  	"code.cloudfoundry.org/cli/util/sorting"
    10  )
    11  
    12  const (
    13  	CommonCommandsIndent string = "  "
    14  	AllCommandsIndent    string = "   "
    15  	CommandIndent        string = "   "
    16  )
    17  
    18  // CommandInfo contains the help details of a command
    19  type CommandInfo struct {
    20  	// Name is the command name
    21  	Name string
    22  
    23  	// Description is the command description
    24  	Description string
    25  
    26  	// Alias is the command alias
    27  	Alias string
    28  
    29  	// Usage is the command usage string
    30  	Usage string
    31  
    32  	// Examples is the command examples string
    33  	Examples string
    34  
    35  	// Resources is the types of object that the command applies to
    36  	Resources string
    37  
    38  	// RelatedCommands is a list of commands related to the command
    39  	RelatedCommands []string
    40  
    41  	// Flags contains the list of flags for this command
    42  	Flags []CommandFlag
    43  
    44  	// Environment is a list of environment variables specific for this command
    45  	Environment []EnvironmentVariable
    46  }
    47  
    48  // CommandFlag contains the help details of a command's flag
    49  type CommandFlag struct {
    50  	// Short is the short form of the flag
    51  	Short string
    52  
    53  	// Long is the long form of the flag
    54  	Long string
    55  
    56  	// Description is the description of the flag
    57  	Description string
    58  
    59  	// Default is the flag's default value
    60  	Default string
    61  }
    62  
    63  // EnvironmentVariable contains env vars specific for this command
    64  type EnvironmentVariable struct {
    65  	Name         string
    66  	Description  string
    67  	DefaultValue string
    68  }
    69  
    70  // HasUsage is an interface that commands may implement if they want to define their usage
    71  // text in a Usage() method, which gives them more flexibility than a struct tag.
    72  type HasUsage interface {
    73  	Usage() string
    74  }
    75  
    76  // HasExamples is an interface that commands may implement if they want to define their examples
    77  // text in a Examples() method, which gives them more flexibility than a struct tag.
    78  type HasExamples interface {
    79  	Examples() string
    80  }
    81  
    82  // HasResources is an interface that commands may implement if they want to define their resources
    83  // text in a Resources() method, which gives them more flexibility than a struct tag.
    84  type HasResources interface {
    85  	Resources() string
    86  }
    87  
    88  // CommandInfoByName returns the help information for a particular commandName in
    89  // the commandList.
    90  func (Actor) CommandInfoByName(commandList interface{}, commandName string) (CommandInfo, error) {
    91  	field, found := reflect.TypeOf(commandList).FieldByNameFunc(
    92  		func(fieldName string) bool {
    93  			field, _ := reflect.TypeOf(commandList).FieldByName(fieldName)
    94  			return field.Tag.Get("command") == commandName || field.Tag.Get("alias") == commandName
    95  		},
    96  	)
    97  
    98  	if !found {
    99  		return CommandInfo{}, actionerror.InvalidCommandError{CommandName: commandName}
   100  	}
   101  
   102  	tag := field.Tag
   103  	cmd := CommandInfo{
   104  		Name:        tag.Get("command"),
   105  		Description: tag.Get("description"),
   106  		Alias:       tag.Get("alias"),
   107  		Flags:       []CommandFlag{},
   108  		Environment: []EnvironmentVariable{},
   109  	}
   110  
   111  	fieldValue := reflect.ValueOf(commandList).FieldByIndex(field.Index)
   112  
   113  	if commandWithUsage, hasUsage := fieldValue.Interface().(HasUsage); hasUsage {
   114  		cmd.Usage = strings.ReplaceAll(
   115  			strings.TrimSpace(commandWithUsage.Usage()),
   116  			"\n",
   117  			"\n"+CommandIndent,
   118  		)
   119  	}
   120  
   121  	if commandWithExamples, hasExamples := fieldValue.Interface().(HasExamples); hasExamples {
   122  		cmd.Examples = strings.ReplaceAll(
   123  			strings.TrimSpace(commandWithExamples.Examples()),
   124  			"\n",
   125  			"\n"+CommandIndent,
   126  		)
   127  	}
   128  
   129  	if commandWithResources, hasResources := fieldValue.Interface().(HasResources); hasResources {
   130  		cmd.Resources = strings.ReplaceAll(
   131  			strings.TrimSpace(commandWithResources.Resources()),
   132  			"\n",
   133  			"\n"+CommandIndent,
   134  		)
   135  	}
   136  
   137  	command := field.Type
   138  	for i := 0; i < command.NumField(); i++ {
   139  		fieldTag := command.Field(i).Tag
   140  
   141  		if fieldTag.Get("hidden") != "" {
   142  			continue
   143  		}
   144  
   145  		if cmd.Usage == "" && fieldTag.Get("usage") != "" {
   146  			cmd.Usage = fieldTag.Get("usage")
   147  			continue
   148  		}
   149  
   150  		if cmd.Examples == "" && fieldTag.Get("examples") != "" {
   151  			cmd.Examples = fieldTag.Get("examples")
   152  			continue
   153  		}
   154  
   155  		if fieldTag.Get("related_commands") != "" {
   156  			relatedCommands := strings.Split(fieldTag.Get("related_commands"), ", ")
   157  			sort.Slice(relatedCommands, sorting.SortAlphabeticFunc(relatedCommands))
   158  			cmd.RelatedCommands = relatedCommands
   159  			continue
   160  		}
   161  
   162  		if fieldTag.Get("description") != "" {
   163  			cmd.Flags = append(cmd.Flags, CommandFlag{
   164  				Short:       fieldTag.Get("short"),
   165  				Long:        fieldTag.Get("long"),
   166  				Description: fieldTag.Get("description"),
   167  				Default:     fieldTag.Get("default"),
   168  			})
   169  		}
   170  
   171  		if fieldTag.Get("environmentName") != "" {
   172  			cmd.Environment = append(cmd.Environment, EnvironmentVariable{
   173  				Name:         fieldTag.Get("environmentName"),
   174  				DefaultValue: fieldTag.Get("environmentDefault"),
   175  				Description:  fieldTag.Get("environmentDescription"),
   176  			})
   177  		}
   178  	}
   179  
   180  	return cmd, nil
   181  }
   182  
   183  // CommandInfos returns a slice of CommandInfo that only fills in
   184  // the Name and Description for all the commands in commandList
   185  func (Actor) CommandInfos(commandList interface{}) map[string]CommandInfo {
   186  	handler := reflect.TypeOf(commandList)
   187  
   188  	infos := make(map[string]CommandInfo, handler.NumField())
   189  	for i := 0; i < handler.NumField(); i++ {
   190  		fieldTag := handler.Field(i).Tag
   191  		commandName := fieldTag.Get("command")
   192  		infos[commandName] = CommandInfo{
   193  			Name:        commandName,
   194  			Description: fieldTag.Get("description"),
   195  			Alias:       fieldTag.Get("alias"),
   196  		}
   197  	}
   198  
   199  	return infos
   200  }