github.com/liamawhite/cli-with-i18n@v6.32.1-0.20171122084555-dede0a5c3448+incompatible/main.go (about) 1 package main 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "reflect" 8 "strings" 9 10 "github.com/jessevdk/go-flags" 11 "github.com/liamawhite/cli-with-i18n/cf/cmd" 12 "github.com/liamawhite/cli-with-i18n/command" 13 "github.com/liamawhite/cli-with-i18n/command/common" 14 "github.com/liamawhite/cli-with-i18n/command/translatableerror" 15 "github.com/liamawhite/cli-with-i18n/command/v2" 16 "github.com/liamawhite/cli-with-i18n/util/configv3" 17 "github.com/liamawhite/cli-with-i18n/util/panichandler" 18 "github.com/liamawhite/cli-with-i18n/util/ui" 19 log "github.com/Sirupsen/logrus" 20 ) 21 22 type UI interface { 23 DisplayError(err error) 24 DisplayWarning(template string, templateValues ...map[string]interface{}) 25 } 26 27 type DisplayUsage interface { 28 DisplayUsage() 29 } 30 31 var ErrFailed = errors.New("command failed") 32 var ParseErr = errors.New("incorrect type for arg") 33 34 func main() { 35 defer panichandler.HandlePanic() 36 parse(os.Args[1:]) 37 } 38 39 func parse(args []string) { 40 parser := flags.NewParser(&common.Commands, flags.HelpFlag) 41 parser.CommandHandler = executionWrapper 42 extraArgs, err := parser.ParseArgs(args) 43 if err == nil { 44 return 45 } 46 47 if flagErr, ok := err.(*flags.Error); ok { 48 switch flagErr.Type { 49 case flags.ErrHelp, flags.ErrUnknownFlag, flags.ErrExpectedArgument, flags.ErrInvalidChoice: 50 _, found := reflect.TypeOf(common.Commands).FieldByNameFunc( 51 func(fieldName string) bool { 52 field, _ := reflect.TypeOf(common.Commands).FieldByName(fieldName) 53 return parser.Active != nil && parser.Active.Name == field.Tag.Get("command") 54 }, 55 ) 56 57 if found && flagErr.Type == flags.ErrUnknownFlag && parser.Active.Name == "set-env" { 58 newArgs := []string{} 59 for _, arg := range args { 60 if arg[0] == '-' { 61 newArgs = append(newArgs, fmt.Sprintf("%s%s", v2.WorkAroundPrefix, arg)) 62 } else { 63 newArgs = append(newArgs, arg) 64 } 65 } 66 parse(newArgs) 67 return 68 } 69 70 if flagErr.Type == flags.ErrUnknownFlag || flagErr.Type == flags.ErrExpectedArgument || flagErr.Type == flags.ErrInvalidChoice { 71 fmt.Fprintf(os.Stderr, "Incorrect Usage: %s\n\n", flagErr.Error()) 72 } 73 74 if found { 75 parse([]string{"help", parser.Active.Name}) 76 } else { 77 switch len(extraArgs) { 78 case 0: 79 parse([]string{"help"}) 80 case 1: 81 if !isOption(extraArgs[0]) || (len(args) > 1 && extraArgs[0] == "-a") { 82 parse([]string{"help", extraArgs[0]}) 83 } else { 84 parse([]string{"help"}) 85 } 86 default: 87 if isCommand(extraArgs[0]) { 88 parse([]string{"help", extraArgs[0]}) 89 } else { 90 parse(extraArgs[1:]) 91 } 92 } 93 } 94 95 if flagErr.Type == flags.ErrUnknownFlag || flagErr.Type == flags.ErrExpectedArgument || flagErr.Type == flags.ErrInvalidChoice { 96 os.Exit(1) 97 } 98 case flags.ErrRequired: 99 fmt.Fprintf(os.Stderr, "Incorrect Usage: %s\n\n", flagErr.Error()) 100 parse([]string{"help", args[0]}) 101 os.Exit(1) 102 case flags.ErrMarshal: 103 errMessage := strings.Split(flagErr.Message, ":") 104 fmt.Fprintf(os.Stderr, "Incorrect Usage: %s\n\n", errMessage[0]) 105 parse([]string{"help", args[0]}) 106 os.Exit(1) 107 case flags.ErrUnknownCommand: 108 cmd.Main(os.Getenv("CF_TRACE"), os.Args) 109 case flags.ErrCommandRequired: 110 if common.Commands.VerboseOrVersion { 111 parse([]string{"version"}) 112 } else { 113 parse([]string{"help"}) 114 } 115 default: 116 fmt.Fprintf(os.Stderr, "Unexpected flag error\ntype: %s\nmessage: %s\n", flagErr.Type, flagErr.Error()) 117 } 118 } else if err == ErrFailed { 119 os.Exit(1) 120 } else if err == ParseErr { 121 fmt.Println() 122 parse([]string{"help", args[0]}) 123 os.Exit(1) 124 } else { 125 fmt.Fprintf(os.Stderr, "Unexpected error: %s\n", err.Error()) 126 os.Exit(1) 127 } 128 } 129 130 func isCommand(s string) bool { 131 _, found := reflect.TypeOf(common.Commands).FieldByNameFunc( 132 func(fieldName string) bool { 133 field, _ := reflect.TypeOf(common.Commands).FieldByName(fieldName) 134 return s == field.Tag.Get("command") || s == field.Tag.Get("alias") 135 }) 136 137 return found 138 } 139 140 func isOption(s string) bool { 141 return strings.HasPrefix(s, "-") 142 } 143 144 func executionWrapper(cmd flags.Commander, args []string) error { 145 cfConfig, configErr := configv3.LoadConfig(configv3.FlagOverride{ 146 Verbose: common.Commands.VerboseOrVersion, 147 }) 148 if configErr != nil { 149 if _, ok := configErr.(translatableerror.EmptyConfigError); !ok { 150 return configErr 151 } 152 } 153 154 commandUI, err := ui.NewUI(cfConfig) 155 if err != nil { 156 return err 157 } 158 159 // TODO: when the line in the old code under `cf` which calls 160 // configv3.LoadConfig() is finally removed, then we should replace the code 161 // path above with the following: 162 // 163 // var configErrTemplate string 164 // if configErr != nil { 165 // if ce, ok := configErr.(translatableerror.EmptyConfigError); ok { 166 // configErrTemplate = ce.Error() 167 // } else { 168 // return configErr 169 // } 170 // } 171 172 // commandUI, err := ui.NewUI(cfConfig) 173 // if err != nil { 174 // return err 175 // } 176 177 // if configErr != nil { 178 // commandUI.DisplayWarning(configErrTemplate, map[string]interface{}{ 179 // "FilePath": configv3.ConfigFilePath(), 180 // }) 181 // } 182 183 defer func() { 184 configWriteErr := configv3.WriteConfig(cfConfig) 185 if configWriteErr != nil { 186 fmt.Fprintf(os.Stderr, "Error writing config: %s", configWriteErr.Error()) 187 } 188 }() 189 190 if extendedCmd, ok := cmd.(command.ExtendedCommander); ok { 191 log.SetOutput(os.Stderr) 192 log.SetLevel(log.Level(cfConfig.LogLevel())) 193 194 err = extendedCmd.Setup(cfConfig, commandUI) 195 if err != nil { 196 return handleError(err, commandUI) 197 } 198 return handleError(extendedCmd.Execute(args), commandUI) 199 } 200 201 return fmt.Errorf("command does not conform to ExtendedCommander") 202 } 203 204 func handleError(err error, commandUI UI) error { 205 if err == nil { 206 return nil 207 } 208 209 commandUI.DisplayError(err) 210 211 if _, ok := err.(DisplayUsage); ok { 212 return ParseErr 213 } 214 215 return ErrFailed 216 }