Skip to content
This repository has been archived by the owner on May 26, 2023. It is now read-only.

Allow extra flags in the config file #29

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ $ cat > gopher.go

import (
"fmt"
"github.com/namsral/flag"
"github.com/robert-zaremba/flag"
)

func main() {
Expand Down Expand Up @@ -83,7 +83,7 @@ It's intended for projects which require a simple configuration made available t
Example:

```go
import "github.com/namsral/flag"
import "github.com/robert-zaremba/flag"

flag.String(flag.DefaultConfigFlagname, "", "path to config file")
flag.Int("age", 24, "help message for age")
Expand Down Expand Up @@ -161,7 +161,7 @@ age=33

For more examples see the [examples][] directory in the project repository.

[examples]: https://github.com/namsral/flag/tree/master/examples
[examples]: https://github.com/robert-zaremba/flag/tree/master/examples

That's it.

Expand Down
2 changes: 1 addition & 1 deletion examples/gopher.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package main

import (
"github.com/namsral/flag"
"github.com/robert-zaremba/flag"
"fmt"
)

Expand Down
17 changes: 0 additions & 17 deletions export_test.go

This file was deleted.

9 changes: 3 additions & 6 deletions extras.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ func (f *FlagSet) ParseFile(path string) error {
f.usage()
return ErrHelp
}
return f.failf("configuration variable provided but not defined: %s", name)
// ignore if extra flag is defined in the config file
continue
}

if fv, ok := flag.Value.(boolFlag); ok && fv.IsBoolFlag() { // special case: doesn't need an arg
Expand All @@ -177,9 +178,5 @@ func (f *FlagSet) ParseFile(path string) error {
f.actual[name] = flag
}

if err := scanner.Err(); err != nil {
return err
}

return nil
return scanner.Err()
}
10 changes: 9 additions & 1 deletion extras_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,17 @@ import (
"testing"
"time"

. "github.com/namsral/flag"
. "github.com/robert-zaremba/flag"
)

// ResetForTesting clears all flag state and sets the usage function as directed.
// After calling ResetForTesting, parse errors in flag handling will not
// exit the program.
func ResetForTesting(usage func()) {
CommandLine = NewFlagSet(os.Args[0], ContinueOnError)
Usage = usage
}

// Test parsing a environment variables
func TestParseEnv(t *testing.T) {

Expand Down
2 changes: 1 addition & 1 deletion flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -871,7 +871,7 @@ func (f *FlagSet) parseOne() (bool, error) {
}
}
m := f.formal
flag, alreadythere := m[name] // BUG
flag, alreadythere := m[name]
if !alreadythere {
if name == "help" || name == "h" { // special case for nice help message.
f.usage()
Expand Down
14 changes: 12 additions & 2 deletions flag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"testing"
"time"

. "github.com/namsral/flag"
. "github.com/robert-zaremba/flag"
)

func boolString(s string) string {
Expand Down Expand Up @@ -379,6 +379,16 @@ func TestHelp(t *testing.T) {
}
}

const defaultOutput2 = " -A=false: for bootstrapping, allow 'any' type\n" +
" -Alongflagname=false: disable bounds checking\n" +
" -C=true: a boolean defaulting to true\n" +
" -D=\"\": set relative `path` for local imports\n" +
" -F=2.7: a non-zero `number`\n" +
" -G=0: a float that defaults to zero\n" +
" -N=27: a non-zero int\n" +
" -Z=0: an int that defaults to zero\n" +
" -maxT=0s: set `timeout` for dial\n"

const defaultOutput = ` -A for bootstrapping, allow 'any' type
-Alongflagname
disable bounds checking
Expand Down Expand Up @@ -413,6 +423,6 @@ func TestPrintDefaults(t *testing.T) {
fs.PrintDefaults()
got := buf.String()
if got != defaultOutput {
t.Errorf("got %q want %q\n", got, defaultOutput)
t.Errorf("got\n%q want\n%q\n", got, defaultOutput)
}
}
10 changes: 10 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module github.com/robert-zaremba/flag

go 1.13

require (
github.com/robert-zaremba/checkers v1.0.1
github.com/robert-zaremba/errstack v1.0.2
github.com/robert-zaremba/go-bat v1.0.1
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f
)
29 changes: 29 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mozillazg/go-unidecode v0.1.1 h1:uiRy1s4TUqLbcROUrnCN/V85Jlli2AmDF6EeAXOeMHE=
github.com/mozillazg/go-unidecode v0.1.1/go.mod h1:fYMdhyjni9ZeEmS6OE/GJHDLsF8TQvIVDwYR/drR26Q=
github.com/namsral/flag v1.7.4-pre h1:b2ScHhoCUkbsq0d2C15Mv+VU8bl8hAXV8arnWiOHNZs=
github.com/namsral/flag v1.7.4-pre/go.mod h1:OXldTctbM6SWH1K899kPZcf65KxJiD7MsceFUpB5yDo=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/robert-zaremba/checkers v1.0.1 h1:AjxF5P+YkOeWsvFSbTcdk/0lvNDDdkzaM3HrEc4XSEE=
github.com/robert-zaremba/checkers v1.0.1/go.mod h1:wUVuqhZje9IKym5bZuW1nbA0GqRRgnYTMehly17F56Q=
github.com/robert-zaremba/errstack v1.0.2 h1:WSNVsQnd3YFLDBW1FysLkJxHkbRsbpgaYk7R8gBJyRQ=
github.com/robert-zaremba/errstack v1.0.2/go.mod h1:KCGDqMDzP5xgeKXM33WryQ8+Zj7EaxnP+J6t3OBhU14=
github.com/robert-zaremba/errstack v3.1.0+incompatible h1:wLAs0u9qr7u4s9UVtCAhx1I4dagUVx5d/MuYhK9sNr4=
github.com/robert-zaremba/errstack v3.1.0+incompatible/go.mod h1:YiMUuTTLstXOe9Ao1J0w0eTf8CaGS+q4WDIJaypSwVI=
github.com/robert-zaremba/go-bat v1.0.1 h1:5C/UryVjj0sKPvT4UPCI2gaIrsXR7sXASMM45ZxCcKo=
github.com/robert-zaremba/go-bat v1.0.1/go.mod h1:GYLn+EyFubUfo/yPrQsma+cri4vCjRC+Qlovomu6sWw=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
30 changes: 30 additions & 0 deletions setup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package flag

import (
"fmt"
"os"
)

// Setup parses flag and setups usage function.
// `positionalArgs` is a string representing positional args, eg: "arg1 arg2 arg3"
// `commands` if provided is a positional argument command description.
func Setup(version, positionalArgs string, commands ...string) {
Usage = func() {
fmt.Fprintln(os.Stderr, "Version: ", version)
if len(commands) != 0 {
fmt.Fprintf(os.Stderr,
"USAGE: %s <commands> <parameters> %s\n\n", os.Args[0], positionalArgs)
fmt.Fprint(os.Stderr, "COMMANDS:", commands[0], "\n\n")
} else {
fmt.Fprintf(os.Stderr,
"USAGE: %s <parameters> %s\n\n", os.Args[0], positionalArgs)
}
fmt.Fprintln(os.Stderr, "PARAMETERS:")
PrintDefaults()
}
Parse()
}
73 changes: 73 additions & 0 deletions srv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package flag

import (
"flag"
"net/url"
"os"

"github.com/robert-zaremba/errstack"
bat "github.com/robert-zaremba/go-bat"
)

// URL is a structure containing network connection details to a service
// The value should have the following structure:
// //username:password@host:port/directory
type URL struct {
url.URL
}

// Set implements github.com/robert-zaremba/flag Value interface
func (a *URL) Set(value string) error {
u, err := url.Parse(value)
if err != nil {
return errstack.WrapAsReq(err, "Can't parse network address config")
}
a.URL = *u
return nil
}

// Path represents a file in a filesystem
type Path struct {
Path string
}

// Set implements github.com/robert-zaremba/flag Value interface
func (a *Path) Set(filePath string) error {
a.Path = filePath
return a.Check()
}

// String implements github.com/robert-zaremba/flag Value interface
func (a *Path) String() string {
return a.Path
}

// Check returns an error if it can't find the file
func (a Path) Check() error {
if a.Path == "" {
return errstack.NewReq("File path can't be empty")
}
_, err := os.Stat(a.Path)
return err
}

// SrvFlags represents common server flags
type SrvFlags struct {
Production *bool
Port *string
}

// NewSrvFlags setups common server flags
func NewSrvFlags() SrvFlags {
return SrvFlags{
flag.Bool("production", false, "Run in production mode"),
flag.String("port", "8000", "The HTTP listening port"),
}
}

// Check validates the flags. It may panic!
func (f SrvFlags) Check() error {
errb := errstack.NewBuilder()
bat.Atoi64Errp(*f.Port, errb.Putter("port"))
return errb.ToReqErr()
}
54 changes: 54 additions & 0 deletions srv_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package flag

import (
"testing"

rzcheck "github.com/robert-zaremba/checkers"
gocheck "gopkg.in/check.v1"
)

// Hook up gocheck into the "go test" runner.
func Test(t *testing.T) { gocheck.TestingT(t) }

type ExtraSuite struct{}

func init() {
gocheck.Suite(&ExtraSuite{})
}

func (s *ExtraSuite) TestValidateDirExists(c *gocheck.C) {
ff := Path{}
err := ff.Set("/tmp")
c.Assert(err, gocheck.IsNil)
c.Assert(ff.String(), gocheck.Equals, "/tmp")
}

func (s *ExtraSuite) TestValidatePermissionDenied(c *gocheck.C) {
ff := Path{}
_ = ff.Set("/root/secret")
err := ff.Check()
c.Assert(err, gocheck.ErrorMatches, "stat /root/secret: permission denied")
}

func (s *ExtraSuite) TestValidateNoFile(c *gocheck.C) {
ff := Path{}
_ = ff.Set("")
err := ff.Check()
c.Assert(err, rzcheck.ErrorContains, "File path can't be empty")
}

func (s *ExtraSuite) TestHandleBadDefaultWithPanic(c *gocheck.C) {
ff := Path{"hello-world"}
err := ff.Check()
c.Assert(err, gocheck.ErrorMatches, "stat hello-world: no such file or directory")
}

func (s *ExtraSuite) TestHandleDefault(c *gocheck.C) {
ff := Path{"/tmp"}
c.Assert(ff.String(), gocheck.Equals, "/tmp")
ff = Path{"/"}
c.Assert(ff.String(), gocheck.Equals, "/")
err := ff.Set("/tmp")
c.Assert(err, gocheck.IsNil)
c.Assert(ff.String(), gocheck.Equals, "/tmp")
}
37 changes: 37 additions & 0 deletions validate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package flag

import (
"flag"
"fmt"
"strings"
)

// Checker is an interface for type which has a Check function
type Checker interface {
Check() error
}

// Validate runs flag checkers
func Validate(positionalArgs string, checkers ...Checker) error {
expected := strings.Fields(positionalArgs)
if len(expected) != flag.NArg() {
return fmt.Errorf("missing required positional arguments: %s. Number of args Provided: %d, expected: %d",
expected, flag.NArg(), len(expected))
}
for _, c := range checkers {
if err := c.Check(); err != nil {
return err
}
}
return nil
}

// CheckMany is a helper function to check many flag components
func CheckMany(checkers ...Checker) error {
for _, c := range checkers {
if err := c.Check(); err != nil {
return err
}
}
return nil
}