diff --git a/flags.go b/flags.go index 4f6089f..10c4017 100644 --- a/flags.go +++ b/flags.go @@ -36,6 +36,10 @@ func parseFlagsToMap(s *setup, args []string) (map[string]string, error) { printHelpAndExit(s) } + if s.conf.VersionString != "" && arg == "--version" { + printVersionAndExit(s) + } + if arg == "--" { // separator that indicates end of flags return result, nil diff --git a/gonfig.go b/gonfig.go index f446c38..353e9ad 100644 --- a/gonfig.go +++ b/gonfig.go @@ -59,6 +59,10 @@ type Conf struct { // HelpDescription is the description to print for the help flag. // By default, this is "show this help menu". HelpDescription string + + // VersionString is the current program version printed when the --version + // flag is passed. If left empty, the --version flag is disabled. + VersionString string } // setup is the struct that keeps track of the state of the program throughout diff --git a/gonfig_test.go b/gonfig_test.go index 4b7c91a..e8af891 100644 --- a/gonfig_test.go +++ b/gonfig_test.go @@ -5,6 +5,7 @@ package gonfig import ( + "bytes" "encoding/hex" "errors" "io/ioutil" @@ -21,9 +22,14 @@ import ( var ( testTimeStr = "2009-11-10T23:00:00Z" testTime *time.Time + testOutput bytes.Buffer ) +type exitCode int + func init() { + helpOutput = &testOutput + exiter = func(i int) { panic(exitCode(i)) } testTime = &time.Time{} if err := testTime.UnmarshalText([]byte(testTimeStr)); err != nil { panic(err) @@ -140,10 +146,12 @@ func TestGonfig(t *testing.T) { conf Conf - config interface{} - shouldError bool - shouldPanic bool - validate func(t *testing.T, config interface{}) + config interface{} + shouldError bool + shouldPanic bool + shouldExit bool + validate func(t *testing.T, config interface{}) + validateOutput func(t *testing.T, output string) }{ { desc: "only defaults", @@ -786,6 +794,21 @@ func TestGonfig(t *testing.T) { config: &TestStruct{}, shouldError: true, }, + { + desc: "show version", + args: []string{"--version"}, + env: map[string]string{}, + conf: Conf{ + FileDisable: true, + VersionString: "v0.0.0", + }, + config: &TestStruct{}, + shouldError: false, + shouldExit: true, + validateOutput: func(t *testing.T, output string) { + assert.Equal(t, output, "v0.0.0\n") + }, + }, } for _, tc := range testCases { @@ -807,16 +830,31 @@ func TestGonfig(t *testing.T) { conf := tc.conf conf.FileDefaultFilename = filename + testOutput.Reset() if tc.shouldPanic { require.Panics(t, func() { Load(tc.config, conf) }) } else if tc.shouldError { require.Error(t, Load(tc.config, conf)) } else { - require.NoError(t, Load(tc.config, conf)) + require.NoError(t, func() error { + defer func() { + r := recover() + // got an exitCode or no panic AND this doesn't match expectations => error + if _, ok := r.(exitCode); (ok || r == nil) && (r != nil) != tc.shouldExit { + t.Errorf("expected to exit? %v, exited? %v", tc.shouldExit, r != nil) + } else if !ok && r != nil { + panic(r) // avoid shadowing actual panics + } + }() + return Load(tc.config, conf) + }()) if tc.validate != nil { tc.validate(t, tc.config) } + if tc.validateOutput != nil { + tc.validateOutput(t, testOutput.String()) + } } }) } diff --git a/help.go b/help.go index 073d10e..2ef5791 100644 --- a/help.go +++ b/help.go @@ -10,8 +10,9 @@ import ( ) const ( - defaultHelpDescription = "print this help menu" - defaultHelpMessage = "Usage of __EXEC__:" + defaultHelpDescription = "print this help menu" + defaultHelpMessage = "Usage of __EXEC__:" + defaultVersionDescription = "print the program version" ) func typeString(t reflect.Type) string { @@ -200,6 +201,10 @@ func writeHelpMessage(s *setup, w io.Writer) { } lines = append(lines, " -h, --help\x00"+helpFlagDesc) + if s.conf.VersionString != "" { + lines = append(lines, " --version\x00"+defaultVersionDescription) + } + message := s.conf.HelpMessage if message == "" { exec := path.Base(os.Args[0]) @@ -219,8 +224,18 @@ func writeHelpMessage(s *setup, w io.Writer) { } } +// used to capture output for tests +var helpOutput io.Writer = os.Stdout +var exiter = os.Exit + // printHelpAndExit prints the help message and exits the program. func printHelpAndExit(s *setup) { - writeHelpMessage(s, os.Stdout) - os.Exit(2) + writeHelpMessage(s, helpOutput) + exiter(2) +} + +// printHelpAndExit prints the help message and exits the program. +func printVersionAndExit(s *setup) { + fmt.Fprintln(helpOutput, s.conf.VersionString) + exiter(0) }