Documentation ¶
Overview ¶
Declaratively create heirarchical command line apps.
Index ¶
- type App
- type AppOpt
- func AddColorFlag() AppOpt
- func AddVersionCommand(version string) AppOpt
- func ConfigFlag(configFlagName flag.Name, scalarOpts []scalar.ScalarOpt[string], ...) AppOpt
- func OverrideHelpFlag(mappings []help.HelpFlagMapping, defaultChoice string, helpFile *os.File, ...) AppOpt
- func SkipValidation() AppOpt
- type LookupFunc
- type ParseResult
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type App ¶
type App struct { // HelpFile contains the file set in OverrideHelpFlag. // HelpFile is part of the public API to allow for easier testing. // HelpFile is never closed by warg, so if setting it to something other than stderr/stdout, // please remember to close HelpFile after using ParseResult.Action (which writes to HelpFile). HelpFile *os.File // contains filtered or unexported fields }
An App contains your defined sections, commands, and flags Create a new App with New()
func New ¶
New builds a new App!
Example ¶
package main import ( "fmt" "os" "go.bbkane.com/warg" "go.bbkane.com/warg/command" "go.bbkane.com/warg/flag" "go.bbkane.com/warg/section" "go.bbkane.com/warg/value/scalar" ) func login(ctx command.Context) error { url := ctx.Flags["--url"].(string) // timeout doesn't have a default value, // so we can't rely on it being passed. timeout, exists := ctx.Flags["--timeout"] if exists { timeout := timeout.(int) fmt.Printf("Logging into %s with timeout %d\n", url, timeout) return nil } fmt.Printf("Logging into %s\n", url) return nil } func main() { app := warg.New( "newAppName", section.New( "work with a fictional blog platform", section.Command( "login", "Login to the platform", login, ), section.Flag( "--timeout", "Optional timeout. Defaults to no timeout", scalar.Int(), ), section.Flag( "--url", "URL of the blog", scalar.String( scalar.Default("https://www.myblog.com"), ), flag.EnvVars("BLOG_URL"), ), section.Section( "comments", "Deal with comments", section.Command( "list", "List all comments", // still prototyping how we want this // command to look, // so use a provided stub action command.DoNothing, ), ), ), ) // normally we would rely on the user to set the environment variable, // bu this is an example err := os.Setenv("BLOG_URL", "https://envvar.com") if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } app.MustRun([]string{"blog.exe", "login"}, os.LookupEnv) }
Output: Logging into https://envvar.com
func (*App) MustRun ¶
func (app *App) MustRun(osArgs []string, osLookupEnv LookupFunc)
MustRun runs the app. Any flag parsing errors will be printed to stderr and os.Exit(64) (EX_USAGE) will be called. Any errors on an Action will be printed to stderr and os.Exit(1) will be called.
func (*App) Parse ¶
func (app *App) Parse(osArgs []string, osLookupEnv LookupFunc) (*ParseResult, error)
Parse parses the args, but does not execute anything.
Example (Flag_value_options) ¶
ExampleApp_Parse_flag_value_options shows a couple combinations of flag/value options. It's also possible to use '--help detailed' to see the current value of a flag and what set it.
package main import ( "fmt" "log" "os" "go.bbkane.com/warg" "go.bbkane.com/warg/command" "go.bbkane.com/warg/config/yamlreader" "go.bbkane.com/warg/flag" "go.bbkane.com/warg/section" "go.bbkane.com/warg/value/scalar" "go.bbkane.com/warg/value/slice" ) func main() { action := func(ctx command.Context) error { // flag marked as Required(), so no need to check for existance scalarVal := ctx.Flags["--scalar-flag"].(string) // flag might not exist in config, so check for existance // TODO: does this panic on nil? sliceVal, sliceValExists := ctx.Flags["--slice-flag"].([]int) fmt.Printf("--scalar-flag: %#v\n", scalarVal) if sliceValExists { fmt.Printf("--slice-flag: %#v\n", sliceVal) } else { fmt.Printf("--slice-flag value not filled!\n") } return nil } app := warg.New( "flag-overrides", section.New( "demo flag overrides", section.Command( command.Name("show"), "Show final flag values", action, command.Flag( "--scalar-flag", "Demo scalar flag", scalar.String( scalar.Choices("a", "b"), scalar.Default("a"), ), flag.ConfigPath("args.scalar-flag"), flag.Required(), ), command.Flag( "--slice-flag", "Demo slice flag", slice.Int( slice.Choices(1, 2, 3), ), flag.Alias("-slice"), flag.ConfigPath("args.slice-flag"), flag.EnvVars("SLICE", "SLICE_ARG"), ), ), ), warg.ConfigFlag( "--config", []scalar.ScalarOpt[string]{ scalar.Default("~/.config/flag-overrides.yaml"), }, yamlreader.New, "path to YAML config file", flag.Alias("-c"), ), ) err := os.WriteFile( "testdata/ExampleFlagValueOptions/config.yaml", []byte( ` args: slice-flag: - 1 - 2 - 3 `), 0644, ) if err != nil { log.Fatalf("write error: %e", err) } app.MustRun([]string{"calc", "-c", "testdata/ExampleFlagValueOptions/config.yaml", "show", "--scalar-flag", "b"}, os.LookupEnv) }
Output: --scalar-flag: "b" --slice-flag: []int{1, 2, 3}
type AppOpt ¶
type AppOpt func(*App)
AppOpt let's you customize the app. Most AppOpts panic if incorrectly called
func AddColorFlag ¶ added in v0.0.16
func AddColorFlag() AppOpt
AddColorFlag adds a "--color" flag to the root section. By convention, this flag will be respected by the different help commands
func AddVersionCommand ¶ added in v0.0.16
AddVersionCommand adds a "version" command to the root section that prints the version passed. Pass an empty string to use .Main.Version from `debug.ReadBuildInfo`, which returns "(devel)" when using `go run`
func ConfigFlag ¶
func ConfigFlag( configFlagName flag.Name, scalarOpts []scalar.ScalarOpt[string], newConfigReader config.NewReader, helpShort flag.HelpShort, flagOpts ...flag.FlagOpt, ) AppOpt
Use ConfigFlag in conjunction with flag.ConfigPath to allow users to override flag defaults with values from a config. This flag will be parsed and any resulting config will be read before other flag value sources.
Example ¶
package main import ( "fmt" "log" "os" "go.bbkane.com/warg" "go.bbkane.com/warg/command" "go.bbkane.com/warg/config/yamlreader" "go.bbkane.com/warg/flag" "go.bbkane.com/warg/section" "go.bbkane.com/warg/value/scalar" "go.bbkane.com/warg/value/slice" ) func exampleConfigFlagTextAdd(ctx command.Context) error { addends := ctx.Flags["--addend"].([]int) sum := 0 for _, a := range addends { sum += a } fmt.Printf("Sum: %d\n", sum) return nil } func main() { app := warg.New( "newAppName", section.New( "do math", section.Command( command.Name("add"), "add integers", exampleConfigFlagTextAdd, command.Flag( flag.Name("--addend"), "Integer to add. Flag is repeatible", slice.Int(), flag.ConfigPath("add.addends"), flag.Required(), ), ), ), warg.ConfigFlag( "--config", []scalar.ScalarOpt[string]{ scalar.Default("~/.config/calc.yaml"), }, yamlreader.New, "path to YAML config file", flag.Alias("-c"), ), ) err := os.WriteFile( "testdata/ExampleConfigFlag/calc.yaml", []byte( ` add: addends: - 1 - 2 - 3 `), 0644, ) if err != nil { log.Fatalf("write error: %e", err) } app.MustRun([]string{"calc", "-c", "testdata/ExampleConfigFlag/calc.yaml", "add"}, os.LookupEnv) }
Output: Sum: 6
func OverrideHelpFlag ¶
func OverrideHelpFlag( mappings []help.HelpFlagMapping, defaultChoice string, helpFile *os.File, flagName flag.Name, flagHelp flag.HelpShort, flagOpts ...flag.FlagOpt, ) AppOpt
OverrideHelpFlag customizes your --help. If you write a custom --help function, you'll want to add it to your app here!
Example ¶
package main import ( "fmt" "os" "go.bbkane.com/warg" "go.bbkane.com/warg/command" "go.bbkane.com/warg/flag" "go.bbkane.com/warg/help" "go.bbkane.com/warg/help/common" "go.bbkane.com/warg/help/detailed" "go.bbkane.com/warg/section" ) func exampleOverrideHelpFlaglogin(_ command.Context) error { fmt.Println("Logging in") return nil } func exampleOverrideHelpFlagCustomCommandHelp(file *os.File, _ *command.Command, _ common.HelpInfo) command.Action { return func(_ command.Context) error { fmt.Fprintln(file, "Custom command help") return nil } } func exampleOverrideHelpFlagCustomSectionHelp(file *os.File, _ *section.SectionT, _ common.HelpInfo) command.Action { return func(_ command.Context) error { fmt.Fprintln(file, "Custom section help") return nil } } func main() { app := warg.New( "newAppName", section.New( "work with a fictional blog platform", section.Command( "login", "Login to the platform", exampleOverrideHelpFlaglogin, ), ), warg.OverrideHelpFlag( []help.HelpFlagMapping{ { Name: "default", CommandHelp: detailed.DetailedCommandHelp, SectionHelp: detailed.DetailedSectionHelp, }, { Name: "custom", CommandHelp: exampleOverrideHelpFlagCustomCommandHelp, SectionHelp: exampleOverrideHelpFlagCustomSectionHelp, }, }, "default", os.Stdout, "--help", "Print help", flag.Alias("-h"), // the flag default should match a name in the HelpFlagMapping ), ) app.MustRun([]string{"blog.exe", "-h", "custom"}, os.LookupEnv) }
Output: Custom section help
func SkipValidation ¶ added in v0.0.13
func SkipValidation() AppOpt
SkipValidation skips (most of) the app's internal consistency checks when the app is created. If used, make sure to call app.Validate() in a test!
type LookupFunc ¶
Look up keys (meant for environment variable parsing) - fulfillable with os.LookupEnv or warg.LookupMap(map)
func LookupMap ¶
func LookupMap(m map[string]string) LookupFunc
LookupMap loooks up keys from a provided map. Useful to mock os.LookupEnv when parsing
type ParseResult ¶
type ParseResult struct { // Path to the command invoked. Does not include executable name (os.Args[0]) Path []string // TODO: consider moving this inside the command.Context // Context holds the parsed information Context command.Context // Action holds the passed command's action to execute. Action command.Action }
ParseResult holds the result of parsing the command line.