Practical Go. Amit Saha
Чтение книги онлайн.

Читать онлайн книгу Practical Go - Amit Saha страница 15

Название: Practical Go

Автор: Amit Saha

Издательство: John Wiley & Sons Limited

Жанр: Программы

Серия:

isbn: 9781119773832

isbn:

СКАЧАТЬ the arguments a command-line application can accept. The first argument to the NewFlagSet() function is the name of the command that will be shown in help messages. The second argument configures what happens when an error is encountered while parsing the command-line arguments; that is, when the fs.Parse() function is called. When the ContinueOnError option is specified, the execution of the program will continue, even if a non- nil error is returned by the Parse() function. This is useful when you want to perform your own processing if there is a parsing error. Other possible values are ExitOnError, which halts the execution of the program, and PanicOnError, which invokes the panic() function. The difference between ExitOnError and PanicOnError is that you can make use of the recover() function in the latter case to perform any cleanup actions before the program terminates.

      The SetOutput() method specifies the writer that will be used by the initialized FlagSet object for writing any diagnostic or output messages. By default, it is set to the standard error, os.Stderr. Setting it to the specified writer, w, allows us write unit tests to verify the behavior.

      Next, we define the first option:

      Next, we call the Parse() function, passing the args[] slice:

      err := fs.Parse(args) if err != nil { return c, err }

      This is the function that reads the elements of the slice and examines them against the flag options defined.

      During the examination, it will attempt to fill in the values indicated in the specified variables, and if there is an error, it will either return an error to the calling function or terminate the execution, depending on the second argument specified to NewFlagSet() function. If a non-nil error is returned, the parseArgs() function returns the empty config object and the error value.

      If a nil error is returned, we check to see if there was any positional argument specified, and if so, we return the object, c, and an error value:

      if fs.NArg() != 0 { return c, errors.New("Positional arguments specified") }

      Since the greeter program doesn't expect any positional arguments to be specified, it checks for that and displays an error if one or more arguments are specified. The NArg() method returns the number of positional arguments after the options have been parsed.

      // chap1/flag-parse/main.go package main import ( "bufio" "errors" "flag" "fmt" "io" "os" ) type config struct { numTimes int } // TODO Insert definition of getName() as Listing 1.1 // TODO Insert definition of greetUser() as Listing 1.1 // TODO Insert definition of runCmd() as Listing 1.1 // TODO Insert definition of validateArgs as Listing 1.1 func parseArgs(w io.Writer, args []string) (config, error) { c := config{} fs := flag.NewFlagSet("greeter", flag.ContinueOnError) fs.SetOutput(w) fs.IntVar(&c.numTimes, "n", 0, "Number of times to greet") err := fs.Parse(args) if err != nil { return c, err } if fs.NArg() != 0 { return c, errors.New("Positional arguments specified") } return c, nil } func main() { c, err := parseArgs(os.Stderr, os.Args[1:]) if err != nil { fmt.Fprintln(os.Stdout, err) os.Exit(1) } err = validateArgs(c) if err != nil { fmt.Fprintln(os.Stdout, err) os.Exit(1) } err = runCmd(os.Stdin, os.Stdout, c) if err != nil { fmt.Fprintln(os.Stdout, err) os.Exit(1) } }

      The config struct type is modified so that it doesn't have the printUsage field since the parseArgs() function now automatically handles the -h or -help argument. Create a new directory, chap1/flag-parse/, and initialize a module inside it:

      Next, save Listing 1.5 to a file called main.go and build it:

      $ go build -o application

      Run the command without specifying any arguments. You will see the following error message:

      $ ./application Must specify a number greater than 0

      Now run the command specifying the -h option:

      $ ./application -h Usage of greeter: -n int Number of times to greet flag: help requested

      The flag parsing logic recognized the -h option and displayed a default usage message consisting of the name that was specified when calling the NewFlagSet() function and the options along with their name, type, and description. The last line of the above output is seen here because when we haven't explicitly defined an -h option, the Parse() function returns an error, which is displayed as part of the error handling logic in main(). In the next section, you will see how we can improve this behavior.

      Next, let's invoke the program specifying a non-integral value for the -n option:

      $ ./application -n abc invalid value "abc" for flag -n: parse error Usage of greeter: -n int Number of times to greet invalid value "abc" for flag -n: parse error

      Note how we automatically get the type validation error since we tried specifying a non-integral value. In addition, note here again that we get the error twice. We will fix this later in the chapter.

      Finally, let's run the program with a valid value for the -n option:

      Testing the Parsing Logic

      The primary change in our greeter program, as compared to the first version, is in how we are parsing the command-line arguments using the flag package. You will notice that you have already written the greeter program, specifically the parseArgs() function, in a unit testing friendly fashion:

      1 A new FlagSet object is created in the function.

      2 Using СКАЧАТЬ