func main() { parser := args.NewParser(args.Name("http-client"), args.Desc("Example http client client")) parser.AddOption("--verbose").Alias("-v").Count(). Help("Be verbose") parser.AddOption("--endpoint").Default("http://localhost:1234").Env("API_ENDPOINT"). Help("The HTTP endpoint our client will talk too") parser.AddCommand("super-chickens", func(subParser *args.ArgParser, data interface{}) int { subParser.AddCommand("create", createChickens) subParser.AddCommand("list", listChickens) subParser.AddCommand("delete", deleteChickens) // Apply our super metadata =) shared := data.(*SharedStruct) shared.Metadata = "super" // Run the sub-commands retCode, err := subParser.ParseAndRun(nil, data) if err != nil { fmt.Println(err.Error()) return 1 } return retCode }) // Add our non super chicken actions parser.AddCommand("create", createChickens) parser.AddCommand("list", listChickens) parser.AddCommand("delete", deleteChickens) // Parse the command line opts, err := parser.ParseArgs(nil) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } // Build our url from what the user passed in parts, err := url.Parse(opts.String("endpoint")) if err != nil { fmt.Fprint(os.Stderr, "url endpoint '%s' is invalid", opts.String("endpoint"), err.Error()) os.Exit(1) } // This is a shows how you can pass in arbitrary struct // data to your sub-commands allows you to share data and resources // with sub-commands with out resorting to global variables super := &SharedStruct{Metadata: "", Url: parts} // Run the command chosen by the user retCode, err := parser.RunCommand(super) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } os.Exit(retCode) }
func main() { var conf Config // Create the parser with program name 'example' // and environment variables prefixed with APP_ parser := args.NewParser(args.Name("demo"), args.EnvPrefix("APP_"), args.Desc("This is a demo app to showcase some features of args")) // Store Integers directly into a struct with a default value parser.AddOption("--power-level").Alias("-p").StoreInt(&conf.PowerLevel). Env("POWER_LEVEL").Default("10000").Help("set our power level") // Command line options can begin with -name, --name or even ++name // Most non word characters are supported parser.AddOption("++config-file").Alias("+c").IsString(). Default("/path/to/config").Help("path to config file") // Use the args.Env() function to define an environment variable // NOTE: Since the parser was passed args.EnvPrefix("APP_") the actual // environment variable name is 'APP_MESSAGE' parser.AddOption("--message").Alias("-m").StoreStr(&conf.Message). Env("MESSAGE").Default("over-ten-thousand").Help("send a message") // Pass a comma separated list of strings and get a []string slice parser.AddOption("--slice").Alias("-s").StoreStringSlice(&conf.StringSlice).Env("LIST"). Default("one,two,three").Help("list of messages") // Count the number of times an option is seen parser.AddOption("--verbose").Alias("-v").Count().StoreInt(&conf.Verbose).Help("be verbose") // Set bool to true if the option is present on the command line parser.AddOption("--debug").Alias("-d").IsTrue().Help("turn on Debug") // Specify the type of the arg with IsInt(), IsString(), IsBool() or IsTrue() parser.AddOption("--help").Alias("-h").IsTrue().Help("show this help message") // Add Required arguments parser.AddArgument("the-question").Required(). StoreStr(&conf.TheQuestion).Help("Before you have an answer") // Add Optional arguments parser.AddArgument("the-answer").IsInt().Default("42"). StoreInt(&conf.TheAnswer).Help("It must be 42") // 'Conf' options are not set via the command line but can be set // via a config file or an environment variable parser.AddConfig("twelve-factor").Env("TWELVE_FACTOR").Help("Demo of config options") // Define a 'database' subgroup db := parser.InGroup("database") // Add command line options to the subgroup db.AddOption("--host").Alias("-dH").StoreStr(&conf.DbHost). Default("localhost").Help("database hostname") // Add subgroup specific config. 'Conf' options are not set via the // command line but can be set via a config file or anything that calls parser.Apply() db.AddConfig("debug").IsTrue().Help("enable database debug") // 'Conf' option names are not allowed to start with a non word character like // '--' or '++' so they can not be confused with command line options db.AddConfig("database").IsString().Default("myDatabase").Help("name of database to use") // If no type is specified, defaults to 'IsString' db.AddConfig("user").Help("database user") db.AddConfig("pass").Help("database password") // Pass our own argument list, or nil to parse os.Args[] opt := parser.ParseArgsSimple(nil) // NOTE: ParseArgsSimple() is just a convenience, you can call // parser.ParseArgs(nil) directly and handle the errors // yourself if you have more complicated use case // Demo default variables in a struct fmt.Printf("Power '%d'\n", conf.PowerLevel) fmt.Printf("Message '%s'\n", conf.Message) fmt.Printf("String Slice '%s'\n", conf.StringSlice) fmt.Printf("DbHost '%s'\n", conf.DbHost) fmt.Printf("TheAnswer '%d'\n", conf.TheAnswer) fmt.Println("") // If user asked for --help or there were no options // passed and none where required if opt.NoArgs() || opt.Bool("help") { parser.PrintHelp() os.Exit(-1) } fmt.Println("") fmt.Println("==================") fmt.Println(" Direct Cast") fmt.Println("==================") // Fetch values by using the Cast functions fmt.Printf("Power '%d'\n", opt.Int("power-level")) fmt.Printf("Message '%s'\n", opt.String("message")) fmt.Printf("String Slice '%s'\n", opt.StringSlice("slice")) fmt.Printf("Verbose '%d'\n", opt.Int("verbose")) fmt.Printf("Debug '%t'\n", opt.Bool("debug")) fmt.Printf("TheAnswer '%d'\n", opt.Int("the-answer")) fmt.Printf("TheAnswer as String '%s'\n", opt.String("the-answer")) fmt.Println("") fmt.Println("==================") fmt.Println(" Database Group") fmt.Println("==================") // Fetch Group values dbAddOption := opt.Group("database") fmt.Printf("CAST DB Host '%s'\n", dbAddOption.String("host")) fmt.Printf("CAST DB Debug '%t'\n", dbAddOption.Bool("debug")) fmt.Printf("CAST DB User '%s'\n", dbAddOption.String("user")) fmt.Printf("CAST DB Pass '%s'\n", dbAddOption.String("pass")) fmt.Println("") iniFile := []byte(` power-level=20000 message=OVER-TEN-THOUSAND! slice=three,four,five,six verbose=5 debug=true [database] debug=false host=mysql.thrawn01.org user=my-username pass=my-password `) // Make configuration simple by reading arguments from an INI file opts, err := parser.FromINI(iniFile) if err != nil { fmt.Println(err.Error()) os.Exit(-1) } fmt.Println("") fmt.Println("==================") fmt.Println("From INI file") fmt.Println("==================") // Values from the config file are used only if the argument is not present // on the commandline fmt.Printf("INI Power '%d'\n", conf.PowerLevel) fmt.Printf("INI Message '%s'\n", conf.Message) fmt.Printf("INI Slice '%s'\n", conf.StringSlice) fmt.Printf("INI Verbose '%d'\n", conf.Verbose) fmt.Printf("INI Debug '%t'\n", opts.Bool("debug")) fmt.Println("") // Create an Options object from a map opts = parser.NewOptionsFromMap( map[string]interface{}{ "int": 1, "bool": true, "string": "one", "endpoints": map[string]interface{}{ "endpoint1": "host1", "endpoint2": "host2", "endpoint3": "host3", }, "deeply": map[string]interface{}{ "nested": map[string]interface{}{ "thing": "foo", }, "foo": "bar", }, }, ) // Small demo of how Group() works with options opts.String("string") // == "one" opts.Group("endpoints") // == *args.Options opts.Group("endpoints").String("endpoint1") // == "host1" opts.Group("endpoints").ToMap() // map[string]interface{} {"endpoint1": "host1", ...} opts.StringMap("endpoints") // map[string]string {"endpoint1": "host1", ...} opts.KeySlice("endpoints") // [ "endpoint1", "endpoint2", ] opts.StringSlice("endpoints") // [ "host1", "host2", "host3" ] // Leaves the door open for IntSlice(), IntMap(), etc.... }
package args_test import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/thrawn01/args" ) var _ = Describe("Options", func() { var opts *args.Options var log *TestLogger BeforeEach(func() { log = NewTestLogger() parser := args.NewParser() parser.SetLog(log) opts = parser.NewOptionsFromMap( map[string]interface{}{ "int": 1, "bool": true, "string": "one", "endpoints": map[string]interface{}{ "endpoint1": "host1", "endpoint2": "host2", "endpoint3": "host3", }, "deeply": map[string]interface{}{ "nested": map[string]interface{}{ "thing": "foo", }, "foo": "bar",
func main() { parser := args.NewParser(args.Name("watch")) parser.AddOption("--bind").Alias("-b").Default("localhost:8080"). Help("Interface to bind the server too") parser.AddOption("--complex-example").Alias("-ce").IsBool(). Help("Run the more complex example") parser.AddOption("--config-file").Alias("-c"). Help("The Config file to load and watch our config from") // Add a connection string to the database group parser.AddOption("--connection-string").InGroup("database").Alias("-cS"). Default("mysql://username@hostname:MyDB"). Help("Connection string used to connect to the database") // Store the password in the config and not passed via the command line parser.AddConfig("password").InGroup("database").Help("database password") // Specify a config file version, when this version number is updated, the user is signaling to the application // that all edits are complete and the application can reload the config parser.AddConfig("version").IsInt().Default("0").Help("config file version") appConf, err := parser.ParseArgs(nil) if err != nil { fmt.Println(err.Error()) os.Exit(-1) } // Simple handler that prints out our config information http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { conf := appConf.GetOpts() db := conf.Group("database") payload, err := json.Marshal(map[string]string{ "bind": conf.String("bind"), "mysql": db.String("connection-string"), "password": conf.Group("database").String("password"), }) if err != nil { fmt.Println("error:", err) } w.Header().Set("Content-Type", "application/json") w.Write(payload) }) var cancelWatch args.WatchCancelFunc if appConf.Bool("complex-example") { cancelWatch = complex(parser) } else { cancelWatch = simple(parser) } // Shut down the watcher when done defer cancelWatch() // Listen and serve requests log.Printf("Listening for requests on %s", appConf.String("bind")) err = http.ListenAndServe(appConf.String("bind"), nil) if err != nil { log.Println(err) os.Exit(-1) } }
. "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/thrawn01/args" ) var _ = Describe("ArgParser", func() { var log *TestLogger BeforeEach(func() { log = NewTestLogger() }) Describe("ArgParser.ParseArgs(nil)", func() { It("Should return error if AddOption() was never called", func() { parser := args.NewParser(args.NoHelp()) _, err := parser.ParseArgs(nil) Expect(err).ToNot(BeNil()) Expect(err.Error()).To(Equal("Must create some options to match with args.AddOption() before calling arg.ParseArgs()")) }) It("Should add Help option if none provided", func() { parser := args.NewParser() _, err := parser.ParseArgs(nil) Expect(err).To(BeNil()) rule := parser.GetRules()[0] Expect(rule.Name).To(Equal("help")) }) }) Describe("ArgParser.AddOption()", func() { cmdLine := []string{"--one", "-two", "++three", "+four", "--power-level"}