flagnames

package module
v1.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 23, 2023 License: MPL-2.0 Imports: 4 Imported by: 6

README

flagnames

What is flagnames

Package flagnames is meant to be used with the standard Go package flag. When flags with long names are defined, flagnames allow these to be abbreviated. So you get short flags as well, "for free". So if your code defines a flag -verbose, then invocations with -v are fine too.

Example:

import (
        "flag"
        "github.com/KarelKubat/flagnames"
)

var (
        verboseFlag = flag.Bool("verbose", false, "increase verbosity")
        idFlag      = flag.Int("id", 0, "ID to process")
        itemFlag    = flag.Int("item", 0, "item number to fetch")
        prefixFlag  = flag.String("prefix", "", "report prefix")
)

func main() {
        // Patch up the short flags into the known flags. That's the only
        // code change you'll need.
        flagnames.Patch()

        flag.Parse()
        // ... handle the flags, perform whatever the program should do
}

This allows for the following invocations:

  • myprog -verbose -id=1 -item=2 -prefix=myprefix: what you'd expect
  • myprog --verbose --id=1 --item=2 --prefix=myprefix: same, but with --
  • myprog -v -p=myprefix: -verbose and -prefix can be abbreviated to their shortest form (-- also works)
  • myprog -id=1 -it=2: the shortest form of -id and -item is 2 characters, abbreviating to -i won't work since it's ambiguous
  • myprog -p myprefix a b c -p: the first -p is expanded to --prefix=myprefix, but the one beyond a b c is left as-is; it is a positional argument given that flags stop at a
  • myprog -p myprefix -- -p: the first -p is again expanded, the second one not as -- indicates end-of-flags

When flagnames can't resolve shortened flags to their longer form, then no expansion happens - and flag.Parse() will fail:

  • myprog -i 1: will print that flag -i is given but not defined (-i could mean -id or -item, and flagnames can't resolve it). There will be a helpful message to the user: flag parsing error: -i can mean multiple flags -id,-item.

The standard flag -help is also automatically handled:

  • myprog -h (or -he, -hel, -help): will call the usual flag.Usage() function, like with the standard myprog -help

The order of actions is important:

  1. First the flags need to be defined
  2. Optionally, flag.SetOutput() can be called, so that flagnames.Patch() sends output to the correct stream.
  3. Then flagnames.Patch() is called (or flagnames.PatchFlagSet() for a specific flag.FlagSet)
  4. Finally flag.Parse() is called.

Synopsis for global flags

// file: test/m1/main.go
package main

import (
        "flag"
        "fmt"

        "github.com/KarelKubat/flagnames"
)

var (
        verboseFlag = flag.Bool("verbose", false, "increase verbosity")
        idFlag      = flag.Int("id", 0, "ID to process")
        itemFlag    = flag.Int("item", 0, "item number to fetch")
        prefixFlag  = flag.String("prefix", "", "report prefix")
)

func main() {
        // Patch up the short flags into the known flags and parse.
        flagnames.Patch()
        flag.Parse()

        // What have we got?
        fmt.Println("Flags:")
        fmt.Println("  verbose =", *verboseFlag)
        fmt.Println("  id =     ", *idFlag)
        fmt.Println("  item =   ", *itemFlag)
        fmt.Println("  prefix = ", *prefixFlag)
        for _, arg := range flag.Args() {
                fmt.Println("Positional argument:", arg)
        }
}

Synopsis for flag.FlagSet usage

// file: test/m2/main.go
package main

import (
	"flag"
	"fmt"
	"log"
	"os"

	"github.com/KarelKubat/flagnames"
)

func main() {
	if err := parseSubCmdFlags(os.Args[1:]); err != nil {
		log.Fatal(err)
	}
}

func parseSubCmdFlags(args []string) error {
	// Create a dedicated flagset and define some options for it.
	fs := flag.NewFlagSet("myprog", flag.ContinueOnError)

	var verboseFlag bool
	fs.BoolVar(&verboseFlag, "verbose", false, "increase verbosity")

	var IDFlag int
	fs.IntVar(&IDFlag, "id", 0, "ID to process")

	var itemFlag int
	fs.IntVar(&itemFlag, "item", 0, "item number to fetch")

	var prefixFlag string
	fs.StringVar(&prefixFlag, "prefix", "", "report prefix")

	// Patch up short flags into the known flags and parse.
	flagnames.Debug = true
	flagnames.PatchFlagSet(fs, &args)
	if err := fs.Parse(args); err != nil {
		return err
	}

	fmt.Println("verbose =", verboseFlag)
	fmt.Println("id      =", IDFlag)
	fmt.Println("item    =", itemFlag)
	fmt.Println("prefix  =", prefixFlag)

	for _, arg := range fs.Args() {
		fmt.Println("positional argument:", arg)
	}

	return nil
}

Debugging

If the flagnames doesn't behave the way you'd expect it to behave, then prior to calling flagnames.Patch(), set flagnames.Debug = true. That will generate debug messages, stating what's going on and why. If the behavior is a bug, then send me that list along with your invocation and what you would expect flagnames to do.

Or better yet, fix the bug and send me a pull request :)

Documentation

Overview

Package flagnames resolves abbreviated flags to their full form, so that the flag package may understand them.

Short example: This program can be called using the flags -v, -ve, -ver etc., all meaning -verbose. Flag -item cannot be abbreviated to -i as this collides with -id; its shortest form is -it. The shortest form for -prefix is -p.

 package main

 import (
	 "flag"
	 "fmt"

	 "github.com/KarelKubat/flagnames"
 )
 var (
	  verboseFlag = flag.Bool("verbose", false, "increase verbosity")
	  idFlag      = flag.Int("id", 0, "ID to process")
	  itemFlag    = flag.Int("item", 0, "item number to fetch")
	  prefixFlag  = flag.String("prefix", "", "report prefix")
 )

 func main() {
	  // Trace what's happening (optional).
	  flagnames.Debug = true
	  // Patch up the short flags into the known flags and parse.
	  flagnames.Patch()
	  flag.Parse()

	  // What have we got?
	  fmt.Println("Flags:")
	  fmt.Println("  verbose =", *verboseFlag)
	  fmt.Println("  id =     ", *idFlag)
	  fmt.Println("  item =   ", *itemFlag)
	  fmt.Println("  prefix = ", *prefixFlag)
	  for _, arg := range flag.Args() {
      fmt.Println("Positional argument:", arg)
   }
 }

Index

Constants

This section is empty.

Variables

View Source
var (
	// Setting Debug to true shows on stdout how the module parses flags.
	Debug bool
)

Functions

func Patch

func Patch()

Patch patches the default (global) flag set, witch is the flag.CommandLine.

func PatchFlagSet

func PatchFlagSet(fs *flag.FlagSet, actualArgs *[]string)

PatchFlagSet patches a flag.FlagSet to a known list of flags.

Types

This section is empty.

Directories

Path Synopsis
test
m1 command
file: test/m1/main.go
file: test/m1/main.go
m2 command
file: test/m2/main.go
file: test/m2/main.go

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL