github.com/loafoe/cli@v7.1.0+incompatible/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 // RelatedCommands is a list of commands related to the command 36 RelatedCommands []string 37 38 // Flags contains the list of flags for this command 39 Flags []CommandFlag 40 41 // Environment is a list of environment variables specific for this command 42 Environment []EnvironmentVariable 43 } 44 45 // CommandFlag contains the help details of a command's flag 46 type CommandFlag struct { 47 // Short is the short form of the flag 48 Short string 49 50 // Long is the long form of the flag 51 Long string 52 53 // Description is the description of the flag 54 Description string 55 56 // Default is the flag's default value 57 Default string 58 } 59 60 // EnvironmentVariable contains env vars specific for this command 61 type EnvironmentVariable struct { 62 Name string 63 Description string 64 DefaultValue string 65 } 66 67 // HasUsage is an interface that commands may implement if they want to define their usage 68 // text in a Usage() method, which gives them more flexibility than a struct tag. 69 type HasUsage interface { 70 Usage() string 71 } 72 73 // HasExamples is an interface that commands may implement if they want to define their examples 74 // text in a Examples() method, which gives them more flexibility than a struct tag. 75 type HasExamples interface { 76 Examples() string 77 } 78 79 // CommandInfoByName returns the help information for a particular commandName in 80 // the commandList. 81 func (Actor) CommandInfoByName(commandList interface{}, commandName string) (CommandInfo, error) { 82 field, found := reflect.TypeOf(commandList).FieldByNameFunc( 83 func(fieldName string) bool { 84 field, _ := reflect.TypeOf(commandList).FieldByName(fieldName) 85 return field.Tag.Get("command") == commandName || field.Tag.Get("alias") == commandName 86 }, 87 ) 88 89 if !found { 90 return CommandInfo{}, actionerror.InvalidCommandError{CommandName: commandName} 91 } 92 93 tag := field.Tag 94 cmd := CommandInfo{ 95 Name: tag.Get("command"), 96 Description: tag.Get("description"), 97 Alias: tag.Get("alias"), 98 Flags: []CommandFlag{}, 99 Environment: []EnvironmentVariable{}, 100 } 101 102 fieldValue := reflect.ValueOf(commandList).FieldByIndex(field.Index) 103 104 if commandWithUsage, hasUsage := fieldValue.Interface().(HasUsage); hasUsage { 105 cmd.Usage = strings.ReplaceAll( 106 strings.TrimSpace(commandWithUsage.Usage()), 107 "\n", 108 "\n"+CommandIndent, 109 ) 110 } 111 112 if commandWithExamples, hasExamples := fieldValue.Interface().(HasExamples); hasExamples { 113 cmd.Examples = strings.ReplaceAll( 114 strings.TrimSpace(commandWithExamples.Examples()), 115 "\n", 116 "\n"+CommandIndent, 117 ) 118 } 119 120 command := field.Type 121 for i := 0; i < command.NumField(); i++ { 122 fieldTag := command.Field(i).Tag 123 124 if fieldTag.Get("hidden") != "" { 125 continue 126 } 127 128 if cmd.Usage == "" && fieldTag.Get("usage") != "" { 129 cmd.Usage = fieldTag.Get("usage") 130 continue 131 } 132 133 if cmd.Examples == "" && fieldTag.Get("examples") != "" { 134 cmd.Examples = fieldTag.Get("examples") 135 continue 136 } 137 138 if fieldTag.Get("related_commands") != "" { 139 relatedCommands := strings.Split(fieldTag.Get("related_commands"), ", ") 140 sort.Slice(relatedCommands, sorting.SortAlphabeticFunc(relatedCommands)) 141 cmd.RelatedCommands = relatedCommands 142 continue 143 } 144 145 if fieldTag.Get("description") != "" { 146 cmd.Flags = append(cmd.Flags, CommandFlag{ 147 Short: fieldTag.Get("short"), 148 Long: fieldTag.Get("long"), 149 Description: fieldTag.Get("description"), 150 Default: fieldTag.Get("default"), 151 }) 152 } 153 154 if fieldTag.Get("environmentName") != "" { 155 cmd.Environment = append(cmd.Environment, EnvironmentVariable{ 156 Name: fieldTag.Get("environmentName"), 157 DefaultValue: fieldTag.Get("environmentDefault"), 158 Description: fieldTag.Get("environmentDescription"), 159 }) 160 } 161 } 162 163 return cmd, nil 164 } 165 166 // CommandInfos returns a slice of CommandInfo that only fills in 167 // the Name and Description for all the commands in commandList 168 func (Actor) CommandInfos(commandList interface{}) map[string]CommandInfo { 169 handler := reflect.TypeOf(commandList) 170 171 infos := make(map[string]CommandInfo, handler.NumField()) 172 for i := 0; i < handler.NumField(); i++ { 173 fieldTag := handler.Field(i).Tag 174 commandName := fieldTag.Get("command") 175 infos[commandName] = CommandInfo{ 176 Name: commandName, 177 Description: fieldTag.Get("description"), 178 Alias: fieldTag.Get("alias"), 179 } 180 } 181 182 return infos 183 }