// This file is part of Gopher2600. // // Gopher2600 is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Gopher2600 is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Gopher2600. If not, see . // Package modalflag is a wrapper for the flag package in the Go standard // library. It provides a convenient method of handling program modes (and // sub-modes) and allows different flags for each mode. // // At it's simplest it can be used as a replacement for the flag package, with // some differences. Whereas, with flag.FlagSet you call Parse() with the array of // strings as the only argument, with modalflag you first NewArgs() with the // array of arguments and then Parse() with no arguments. For example (note // that no error handling of the Parse() function is shown here): // // md = Modes{Output: os.Stdout} // md.NewArgs(os.Args[1:]) // _, _ = md.Parse() // // The reason for his difference is to allow effective parsing of modes and // sub-modes. We'll come to program modes in a short while. // // In the above example, once the arguments have been parsed, non-flag arguments // can be retrieved with the RemainingArgs() or GetArg() function. For example, // handling exactly one argument: // // switch len(md.RemainingArgs()) { // case 0: // return fmt.Errorf("argument required") // case 1: // Process(md.GetArg(0)) // default: // return fmt.Errorf("too many arguments") // } // // Adding flags is similar to the flag package. Adding a boolean flag: // // verbose := md.AddBool("verbose", false, "print additional log messages") // // These flag functions return a pointer to a variable of the specified type. The // initial value of these variables if the default value, the second argument in // the function call above. The Parse() function will set these values // appropriately according what the user has requested, for example: // // if *verbose { // fmt.Println(additionalLogMessage) // } // // The most important difference between the standard flag package and the // modalflag package is the ability of the latter to handle "modes". In this // context, a mode is a special command line argument that when specified, puts // the program into a different mode of operation. The best and most relevant // example I can think of is the go command. The go command has many different // modes: build, doc, get, test, etc. Each of these modes are different enough // to require a different set of flags and expected arguments. // // The modalflag package handles sub-modes with the AddSubModes() function. // This function takes any number of string arguments, each one the name of a // mode. // // md.AddSubModes("run", "test", "debug") // // For simplicity, all sub-mode comparisons are case insensitive. // // Subsequent calls to Parse() will then process flags in the normal way but // unlike the regular flag.Parse() function will check to see if the first // argument after the flags is one of these modes. If it is, then the // RemainingArgs() function will return all the arguments after the flags AND // the mode selector. // // So, for example: // // md.Parse() // switch md.Mode() { // case "RUN": // runMode(*verbose) // default: // fmt.Printf("%s not yet implemented", md.Mode()) // } // // Now that we've decided on what mode we're in, we can again call Parse() to // process the remaining arguments. This example shows how we can handle return // state and errors from the Parse() function: // // func runMode(verbose bool) { // md.NewMode() // md.AddDuration("runtime", time.ParseDuration("10s"), "max run time") // p, err := md.Parse // switch p { // case ParseError: // fmt.Println(err) // return // case ParseHelp: // return // } // doRun(md.RemainingArguments) // } // // This second call to Parse() will check for any additional flags and any // further sub-modes (none in this example). // // We can chain modes together as deep as we want. For example, the "test" mode // added above could be divided into several different modes: // // md = Modes{Output: os.Stdout} // md.NewArgs(os.Args[1:]) // md.AddSubModes("run", "test", "debug") // _, _ = md.Parse() // switch md.Mode() { // case "TEST": // md.NewMode() // md.AddSubModes("A", "B", "C") // _, _ = md.Parse() // switch md.Mode() { // case "A": // testA() // case "B": // testB() // case "C": // testC() // } // default: // fmt.Printf("%s not yet implemented", md.Mode()) // } // package modalflag