From cca00f48aed905ba3b5c0d6257bccf902dc8c826 Mon Sep 17 00:00:00 2001 From: gdm85 Date: Fri, 14 Feb 2020 19:52:53 +0100 Subject: [PATCH 1/3] Add go.mod --- go.mod | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 go.mod diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3d735ca --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/voxelbrain/goptions + +go 1.13 From ce439bd65ce6d76ea405f4b950a04af2c1464c30 Mon Sep 17 00:00:00 2001 From: gdm85 Date: Fri, 14 Feb 2020 21:26:34 +0100 Subject: [PATCH 2/3] Add support for embedded structs --- flagset.go | 46 +++++++++++++++++++++++++++++++++------------- parse_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/flagset.go b/flagset.go index cf31038..581957d 100644 --- a/flagset.go +++ b/flagset.go @@ -61,39 +61,61 @@ func newFlagset(name string, structValue reflect.Value, parent *FlagSet) *FlagSe r.remainderFlag = parent.remainderFlag } + r.parseStruct(structValue, &once) + + r.createMaps() + return r +} + +func (r *FlagSet) parseStruct(structValue reflect.Value, once *sync.Once) { var i int + l := structValue.Type().NumField() // Parse Option fields - for i = 0; i < structValue.Type().NumField(); i++ { + for i = 0; i < l; i++ { + fieldValue := structValue.Field(i) // Skip unexported fields if StartsWithLowercase(structValue.Type().Field(i).Name) { + // check if it is an embedded type + if structValue.Type().Field(i).Anonymous { + r.parseStruct(fieldValue, once) + } continue } - fieldValue := structValue.Field(i) tag := structValue.Type().Field(i).Tag.Get("goptions") flag, err := parseStructField(fieldValue, tag) - if err != nil { panic(fmt.Sprintf("Invalid struct field: %s", err)) } - if fieldValue.Type().Name() == "Verbs" { + + matched := true + switch fieldValue.Type().Name() { + default: + matched = false + case "Verbs": r.verbFlag = flag - break - } - if fieldValue.Type().Name() == "Help" { + goto ParseVerb + case "Help": r.helpFlag = flag - } - if fieldValue.Type().Name() == "Remainder" && r.remainderFlag == nil { - r.remainderFlag = flag + case "Remainder": + if r.remainderFlag == nil { + r.remainderFlag = flag + } } if len(tag) != 0 { r.Flags = append(r.Flags, flag) + } else if !matched { + // check if it is an embedded type + if structValue.Type().Field(i).Anonymous { + r.parseStruct(fieldValue, once) + } } } +ParseVerb: // Parse verb fields - for i++; i < structValue.Type().NumField(); i++ { + for i++; i < l; i++ { once.Do(func() { r.Verbs = make(map[string]*FlagSet) }) @@ -101,8 +123,6 @@ func newFlagset(name string, structValue reflect.Value, parent *FlagSet) *FlagSe tag := structValue.Type().Field(i).Tag.Get("goptions") r.Verbs[tag] = newFlagset(tag, fieldValue, r) } - r.createMaps() - return r } var ( diff --git a/parse_test.go b/parse_test.go index 91b2aa3..539b08f 100644 --- a/parse_test.go +++ b/parse_test.go @@ -450,3 +450,31 @@ func TestParse_DashAsRemainder(t *testing.T) { } } + +func TestParse_Embedded(t *testing.T) { + type innerT struct { + Name string `goptions:"--name, -n"` + } + + var args []string + var err error + var fs *FlagSet + var options struct { + innerT + Job string `goptions:"--job, -j"` + } + expectedName, expectedJob := "SomeName", "Painter" + + args = []string{"--name", "SomeName", "--job", "Painter"} + fs = NewFlagSet("goptions", &options) + err = fs.Parse(args) + if err != nil { + t.Fatalf("Flag parsing failed: %s", err) + } + if options.Name != expectedName { + t.Fatalf("Expected %s for options.Name, got %s", expectedName, options.Name) + } + if options.Job != expectedJob { + t.Fatalf("Expected %s for options.Job, got %s", expectedJob, options.Job) + } +} From f840344cf78e4f2898e9e995e1e5c4daeaa454ba Mon Sep 17 00:00:00 2001 From: gdm85 Date: Fri, 14 Feb 2020 21:27:52 +0100 Subject: [PATCH 3/3] Test & Build with GitHub actions --- .github/workflows/go.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/go.yml diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..ee6cd11 --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,31 @@ +name: Go +on: [push, pull_request] +jobs: + + build: + name: Build + runs-on: ubuntu-latest + steps: + + - name: Set up Go 1.13 + uses: actions/setup-go@v1 + with: + go-version: 1.13 + id: go + + - name: Check out code into the Go module directory + uses: actions/checkout@v1 + + - name: Test + run: go test -v ./... + + - name: Build + run: go build ./... + + - name: Cache + uses: actions/cache@v1.0.3 + with: + # A directory to store and save the cache + path: ~/go/pkg/mod + # An explicit key for restoring and saving the cache + key: ${{ runner.os }}-${{ hashFiles('**/go.mod') }}