diff --git a/README.adoc b/README.adoc index c9105ba..702c210 100644 --- a/README.adoc +++ b/README.adoc @@ -27,6 +27,8 @@ toc::[] package main import ( + "context" + "errors" "fmt" "io" "log" @@ -35,40 +37,62 @@ import ( "github.com/DavidGamba/go-getoptions" ) -var logger = log.New(io.Discard, "DEBUG: ", log.LstdFlags) +var Logger = log.New(os.Stderr, "", log.LstdFlags) func main() { - var debug bool - var greetCount int - var list map[string]string + os.Exit(program(os.Args)) +} + +func program(args []string) int { + ctx, cancel, done := getoptions.InterruptContext() + defer func() { cancel(); <-done }() + opt := getoptions.New() - opt.Bool("help", false, opt.Alias("h", "?")) - opt.BoolVar(&debug, "debug", false) - opt.IntVar(&greetCount, "greet", 0, - opt.Required(), - opt.Description("Number of times to greet.")) - opt.StringMapVar(&list, "list", 1, 99, - opt.Description("Greeting list by language.")) - remaining, err := opt.Parse(os.Args[1:]) - if opt.Called("help") { - fmt.Fprintf(os.Stderr, opt.Help()) - os.Exit(1) + opt.Self("myscript", "Simple demo script") + opt.Bool("debug", false, opt.GetEnv("DEBUG")) + opt.Int("greet", 0, opt.Required(), opt.Description("Number of times to greet.")) + opt.StringMap("list", 1, 99, opt.Description("Greeting list by language.")) + opt.Bool("quiet", false, opt.GetEnv("QUIET")) + opt.HelpSynopsisArg("", "Name to greet.") + opt.SetCommandFn(Run) + opt.HelpCommand("help", opt.Alias("?")) + remaining, err := opt.Parse(args[1:]) + if err != nil { + fmt.Fprintf(os.Stderr, "ERROR: %s\n", err) + return 1 + } + if opt.Called("quiet") { + Logger.SetOutput(io.Discard) } + + err = opt.Dispatch(ctx, remaining) if err != nil { - fmt.Fprintf(os.Stderr, "ERROR: %s\n\n", err) - fmt.Fprintf(os.Stderr, opt.Help(getoptions.HelpSynopsis)) - os.Exit(1) + if errors.Is(err, getoptions.ErrorHelpCalled) { + return 1 + } + fmt.Fprintf(os.Stderr, "ERROR: %s\n", err) + if errors.Is(err, getoptions.ErrorParsing) { + fmt.Fprintf(os.Stderr, "\n"+opt.Help()) + } + return 1 } + return 0 +} - // Use the passed command line options... Enjoy! - if debug { - logger.SetOutput(os.Stderr) +func Run(ctx context.Context, opt *getoptions.GetOpt, args []string) error { + // Get arguments and options + name, _, err := opt.GetRequiredArg(args) + if err != nil { + return err } - logger.Printf("Unhandled CLI args: %v\n", remaining) + greetCount := opt.Value("greet").(int) + list := opt.Value("list").(map[string]string) + + Logger.Printf("Running: %v", args) // Use the int variable for i := 0; i < greetCount; i++ { - fmt.Println("Hello World, from go-getoptions!") + fmt.Printf("Hello %s, from go-getoptions!\n", name) } // Use the map[string]string variable @@ -78,6 +102,8 @@ func main() { fmt.Printf("\t%s=%s\n", k, v) } } + + return nil } ---- @@ -85,61 +111,73 @@ func main() { + .Show help ---- -$ ./myscript --help +$ ./myscript help +NAME: + myscript - Simple demo script + SYNOPSIS: - myscript --greet [--debug] [--help|-h|-?] [--list ...]... + myscript --greet [--debug] [--help|-?] [--list ...]... + [--quiet] + +ARGUMENTS: + Name to greet. REQUIRED PARAMETERS: - --greet Number of times to greet. + --greet Number of times to greet. OPTIONS: - --debug (default: false) + --debug (default: false, env: DEBUG) - --help|-h|-? (default: false) + --help|-? (default: false) - --list ... Greeting list by language. (default: {}) + --list ... Greeting list by language. (default: {}) + --quiet (default: false, env: QUIET) ---- + .Show errors ---- $ ./myscript -ERROR: Missing required option 'greet'! - -SYNOPSIS: - myscript --greet [--debug] [--help|-h|-?] [--list ...]... +ERROR: Missing required parameter 'greet' ---- + .Show errors ---- $ ./myscript -g ERROR: Missing argument for option 'greet'! - +---- ++ +.Show errors +---- +$ ./myscript -g 3 +ERROR: Missing SYNOPSIS: - myscript --greet [--debug] [--help|-h|-?] [--list ...]... + myscript --greet [--debug] [--help|-?] [--list ...]... + [--quiet] ---- + .Use of int option ---- -$ ./myscript -g 3 -Hello World, from go-getoptions! -Hello World, from go-getoptions! -Hello World, from go-getoptions! +$ ./myscript -g 3 David +2024/01/04 23:25:14 Running: [David] +Hello David, from go-getoptions! +Hello David, from go-getoptions! +Hello David, from go-getoptions! ---- + .Use of bool option ---- -$ ./myscript --debug -g 1 other stuff -DEBUG: 2019/07/14 23:20:22 Unhandled CLI args: [other stuff] -Hello World, from go-getoptions! +$ ./myscript -g 1 David --quiet +Hello David, from go-getoptions! ---- + .Use of map option ---- -./myscript -g 0 -l en='Hello World' es='Hola Mundo' +$ ./myscript -g 0 David -l en='Hello World' es='Hola Mundo' +2024/01/04 23:27:00 Running: [David] Greeting List: - en=Hello World - es=Hola Mundo + en=Hello World + es=Hola Mundo ---- NOTE: If you are starting a new project, instead of copying the example code from above, use the code from the link:./docs/new-project-templates.adoc[New Project Templates]. @@ -545,7 +583,7 @@ This can be used, to print the help only in cases where the user didn't enter va The built in help shows default values and environment variables when available. -It separates _ARGUMENTS_, _REQUIRED PARAMETERS_ and _OPTIONS_ into separate sections. +It separates _COMMANDS_, _ARGUMENTS_, _REQUIRED PARAMETERS_ and _OPTIONS_ into separate sections. For example, the following is a script using the built in help: diff --git a/examples/myscript/main.go b/examples/myscript/main.go index 3e538e7..989fbb1 100644 --- a/examples/myscript/main.go +++ b/examples/myscript/main.go @@ -1,6 +1,8 @@ package main import ( + "context" + "errors" "fmt" "io" "log" @@ -9,45 +11,62 @@ import ( "github.com/DavidGamba/go-getoptions" ) -var Logger = log.New(io.Discard, "DEBUG: ", log.LstdFlags) +var Logger = log.New(os.Stderr, "", log.LstdFlags) func main() { os.Exit(program(os.Args)) } func program(args []string) int { - var debug bool - var greetCount int - var list map[string]string + ctx, cancel, done := getoptions.InterruptContext() + defer func() { cancel(); <-done }() + opt := getoptions.New() opt.Self("myscript", "Simple demo script") - opt.Bool("help", false, opt.Alias("h", "?")) - opt.BoolVar(&debug, "debug", false, opt.GetEnv("DEBUG")) - opt.IntVar(&greetCount, "greet", 0, - opt.Required(), - opt.Description("Number of times to greet.")) - opt.StringMapVar(&list, "list", 1, 99, - opt.Description("Greeting list by language.")) + opt.Bool("debug", false, opt.GetEnv("DEBUG")) + opt.Int("greet", 0, opt.Required(), opt.Description("Number of times to greet.")) + opt.StringMap("list", 1, 99, opt.Description("Greeting list by language.")) + opt.Bool("quiet", false, opt.GetEnv("QUIET")) + opt.HelpSynopsisArg("", "Name to greet.") + opt.SetCommandFn(Run) + opt.HelpCommand("help", opt.Alias("?")) remaining, err := opt.Parse(args[1:]) - if opt.Called("help") { - fmt.Fprint(os.Stderr, opt.Help()) + if err != nil { + fmt.Fprintf(os.Stderr, "ERROR: %s\n", err) return 1 } + if opt.Called("quiet") { + Logger.SetOutput(io.Discard) + } + + err = opt.Dispatch(ctx, remaining) if err != nil { - fmt.Fprintf(os.Stderr, "ERROR: %s\n\n", err) - fmt.Fprint(os.Stderr, opt.Help(getoptions.HelpSynopsis)) + if errors.Is(err, getoptions.ErrorHelpCalled) { + return 1 + } + fmt.Fprintf(os.Stderr, "ERROR: %s\n", err) + if errors.Is(err, getoptions.ErrorParsing) { + fmt.Fprintf(os.Stderr, "\n"+opt.Help()) + } return 1 } + return 0 +} - // Use the passed command line options... Enjoy! - if debug { - Logger.SetOutput(os.Stderr) +func Run(ctx context.Context, opt *getoptions.GetOpt, args []string) error { + // Get arguments and options + name, _, err := opt.GetRequiredArg(args) + if err != nil { + return err } - Logger.Printf("Unhandled CLI args: %v\n", remaining) + greetCount := opt.Value("greet").(int) + list := opt.Value("list").(map[string]string) + + Logger.Printf("Running: %v", args) // Use the int variable for i := 0; i < greetCount; i++ { - fmt.Println("Hello World, from go-getoptions!") + fmt.Printf("Hello %s, from go-getoptions!\n", name) } // Use the map[string]string variable @@ -57,5 +76,6 @@ func program(args []string) int { fmt.Printf("\t%s=%s\n", k, v) } } - return 0 + + return nil }