Merge pull request #38 from alexflint/version_string
Add support for version strings
This commit is contained in:
commit
e6fdb157e9
22
README.md
22
README.md
|
@ -166,6 +166,28 @@ usage: samples [--foo FOO] [--bar BAR]
|
|||
error: you must provide one of --foo and --bar
|
||||
```
|
||||
|
||||
### Version strings
|
||||
|
||||
```go
|
||||
type args struct {
|
||||
...
|
||||
}
|
||||
|
||||
func (args) Version() string {
|
||||
return "someprogram 4.3.0"
|
||||
}
|
||||
|
||||
func main() {
|
||||
var args args
|
||||
arg.MustParse(&args)
|
||||
}
|
||||
```
|
||||
|
||||
```shell
|
||||
$ ./example --version
|
||||
someprogram 4.3.0
|
||||
```
|
||||
|
||||
### Custom parsing
|
||||
|
||||
You can implement your own argument parser by implementing `encoding.TextUnmarshaler`:
|
||||
|
|
43
parse.go
43
parse.go
|
@ -27,6 +27,9 @@ type spec struct {
|
|||
// ErrHelp indicates that -h or --help were provided
|
||||
var ErrHelp = errors.New("help requested by user")
|
||||
|
||||
// ErrVersion indicates that --version was provided
|
||||
var ErrVersion = errors.New("version requested by user")
|
||||
|
||||
// MustParse processes command line arguments and exits upon failure
|
||||
func MustParse(dest ...interface{}) *Parser {
|
||||
p, err := NewParser(Config{}, dest...)
|
||||
|
@ -39,6 +42,10 @@ func MustParse(dest ...interface{}) *Parser {
|
|||
p.WriteHelp(os.Stdout)
|
||||
os.Exit(0)
|
||||
}
|
||||
if err == ErrVersion {
|
||||
fmt.Println(p.version)
|
||||
os.Exit(0)
|
||||
}
|
||||
if err != nil {
|
||||
p.Fail(err.Error())
|
||||
}
|
||||
|
@ -61,14 +68,28 @@ type Config struct {
|
|||
|
||||
// Parser represents a set of command line options with destination values
|
||||
type Parser struct {
|
||||
spec []*spec
|
||||
config Config
|
||||
spec []*spec
|
||||
config Config
|
||||
version string
|
||||
}
|
||||
|
||||
// Versioned is the interface that the destination struct should implement to
|
||||
// make a version string appear at the top of the help message.
|
||||
type Versioned interface {
|
||||
// Version returns the version string that will be printed on a line by itself
|
||||
// at the top of the help message.
|
||||
Version() string
|
||||
}
|
||||
|
||||
// NewParser constructs a parser from a list of destination structs
|
||||
func NewParser(config Config, dests ...interface{}) (*Parser, error) {
|
||||
var specs []*spec
|
||||
p := Parser{
|
||||
config: config,
|
||||
}
|
||||
for _, dest := range dests {
|
||||
if dest, ok := dest.(Versioned); ok {
|
||||
p.version = dest.Version()
|
||||
}
|
||||
v := reflect.ValueOf(dest)
|
||||
if v.Kind() != reflect.Ptr {
|
||||
panic(fmt.Sprintf("%s is not a pointer (did you forget an ampersand?)", v.Type()))
|
||||
|
@ -138,19 +159,16 @@ func NewParser(config Config, dests ...interface{}) (*Parser, error) {
|
|||
}
|
||||
}
|
||||
}
|
||||
specs = append(specs, &spec)
|
||||
p.spec = append(p.spec, &spec)
|
||||
}
|
||||
}
|
||||
if config.Program == "" {
|
||||
config.Program = "program"
|
||||
if p.config.Program == "" {
|
||||
p.config.Program = "program"
|
||||
if len(os.Args) > 0 {
|
||||
config.Program = filepath.Base(os.Args[0])
|
||||
p.config.Program = filepath.Base(os.Args[0])
|
||||
}
|
||||
}
|
||||
return &Parser{
|
||||
spec: specs,
|
||||
config: config,
|
||||
}, nil
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
// Parse processes the given command line option, storing the results in the field
|
||||
|
@ -161,6 +179,9 @@ func (p *Parser) Parse(args []string) error {
|
|||
if arg == "-h" || arg == "--help" {
|
||||
return ErrHelp
|
||||
}
|
||||
if arg == "--version" {
|
||||
return ErrVersion
|
||||
}
|
||||
if arg == "--" {
|
||||
break
|
||||
}
|
||||
|
|
7
usage.go
7
usage.go
|
@ -29,6 +29,10 @@ func (p *Parser) WriteUsage(w io.Writer) {
|
|||
}
|
||||
}
|
||||
|
||||
if p.version != "" {
|
||||
fmt.Fprintln(w, p.version)
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "usage: %s", p.config.Program)
|
||||
|
||||
// write the option component of the usage message
|
||||
|
@ -97,6 +101,9 @@ func (p *Parser) WriteHelp(w io.Writer) {
|
|||
|
||||
// write the list of built in options
|
||||
printOption(w, &spec{boolean: true, long: "help", short: "h", help: "display this help and exit"})
|
||||
if p.version != "" {
|
||||
printOption(w, &spec{boolean: true, long: "version", help: "display version and exit"})
|
||||
}
|
||||
}
|
||||
|
||||
func printOption(w io.Writer, spec *spec) {
|
||||
|
|
|
@ -100,3 +100,32 @@ options:
|
|||
p.WriteHelp(&help)
|
||||
assert.Equal(t, expectedHelp, help.String())
|
||||
}
|
||||
|
||||
type versioned struct{}
|
||||
|
||||
// Version returns the version for this program
|
||||
func (versioned) Version() string {
|
||||
return "example 3.2.1"
|
||||
}
|
||||
|
||||
func TestUsageWithVersion(t *testing.T) {
|
||||
expectedHelp := `example 3.2.1
|
||||
usage: example
|
||||
|
||||
options:
|
||||
--help, -h display this help and exit
|
||||
--version display version and exit
|
||||
`
|
||||
os.Args[0] = "example"
|
||||
p, err := NewParser(Config{}, &versioned{})
|
||||
require.NoError(t, err)
|
||||
|
||||
var help bytes.Buffer
|
||||
p.WriteHelp(&help)
|
||||
actual := help.String()
|
||||
t.Logf("Expected:\n%s", expectedHelp)
|
||||
t.Logf("Actual:\n%s", actual)
|
||||
if expectedHelp != actual {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue