Skip to content

Commit 239eda1

Browse files
authored
Merge pull request #34 from phelmkamp/chain
add `chain` option
2 parents 6a73ab8 + b048ee3 commit 239eda1

File tree

9 files changed

+96
-56
lines changed

9 files changed

+96
-56
lines changed

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,13 @@ Always uses pointer receiver.
5353
`filter` (slice only)
5454

5555
Generates a method that returns a copy of the slice, omitting elements that are rejected by the given function.
56-
Method name is `Filter`, followed by the name of the field unless `omitfield` is specified.
56+
Method name is `Filter` followed by the name of the field.
5757
Uses value receiver by default.
5858

59+
Options
60+
* `omitfield`: exclude field name from method (i.e. just `Filter`)
61+
* `chain`: store result in-place and return the receiver (facilitates method chaining)
62+
5963
`mapper,$type` (slice only)
6064

6165
Generates a method that returns the result of mapping all elements to the specified type using the given function.
@@ -67,7 +71,11 @@ Uses value receiver by default.
6771
Generates `Len` and `Swap` methods to implement [sort.Interface](https://golang.org/pkg/sort/#Interface), along with a `Sort` convenience method. Include the `stringer` option to generate a `Less` method that compares elements by their string representations. Otherwise, a `Less` method must be implemented separately.
6872
Uses value receivers by default.
6973

70-
Tip: Wrap the slice in a struct to take full advantage. See [person.Persons](internal/testdata/person/person.go) for an example.
74+
`wrapper` (slice only)
75+
76+
Indicates that the struct is a "wrapper" for the given slice. Enables `omitfield` and `chain` options for all subsequent directives.
77+
78+
See [person.Persons](internal/testdata/person/person.go) for an example.
7179

7280
`stringer`
7381

directive/directive.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const (
1313
optOmitField = "omitfield"
1414
optStringer = "stringer"
1515
optReflect = "reflect"
16+
optChain = "chain"
1617
)
1718

1819
var (
@@ -35,6 +36,7 @@ type Target struct {
3536
RcvName, RcvType string
3637
FldNames []string
3738
FldType string
39+
DfltOpts []string
3840
}
3941

4042
type runFunc func(*Target, []string)
@@ -48,9 +50,17 @@ func RunAll(ds []string, tgt *Target) {
4850

4951
// Run runs the given directive.
5052
func Run(d string, tgt *Target) {
53+
if d == "wrapper" {
54+
// enable options and continue
55+
tgt.DfltOpts = append(tgt.DfltOpts, optOmitField)
56+
tgt.DfltOpts = append(tgt.DfltOpts, optChain)
57+
return
58+
}
59+
5160
opts := strings.Split(d, ",")
5261
d = opts[0]
5362
opts = opts[1:]
63+
opts = append(opts, tgt.DfltOpts...)
5464

5565
run, ok := runFuncs[d]
5666
if !ok {
@@ -120,12 +130,10 @@ func setter(tgt *Target, opts []string) {
120130
func filter(tgt *Target, opts []string) {
121131
elemType := strings.TrimPrefix(tgt.FldType, "[]")
122132

123-
var isOmitField bool
133+
var isOmitField, isChain bool
124134
for i := range opts {
125-
if opts[i] == optOmitField {
126-
isOmitField = true
127-
break
128-
}
135+
isOmitField = isOmitField || opts[i] == optOmitField
136+
isChain = isChain || opts[i] == optChain
129137
}
130138

131139
for _, fldNm := range tgt.FldNames {
@@ -135,15 +143,22 @@ func filter(tgt *Target, opts []string) {
135143
method += upperFirst(fldNm)
136144
}
137145

146+
retVals, retStmt := tgt.FldType, "return result"
147+
if isChain {
148+
retVals = tgt.RcvType
149+
retStmt = fmt.Sprintf("%s.%s = result\n\treturn %s", tgt.RcvName, fldNm, tgt.RcvName)
150+
}
151+
138152
log.Printf("Adding method: %s\n", method)
139153
filter := meta.Method{
140154
RcvName: tgt.RcvName,
141155
RcvType: tgt.RcvType,
142156
Name: method,
143157
ArgType: elemType,
144-
RetVals: tgt.FldType,
158+
RetVals: retVals,
145159
FldName: fldNm,
146160
FldType: tgt.FldType,
161+
Misc: map[string]interface{}{"RetStmt": retStmt},
147162
Tmpl: "filter",
148163
}
149164
tgt.MetaFile.Methods = append(tgt.MetaFile.Methods, &filter)

internal/testdata/foobar/foo_meta.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
package foobar
55

66
import (
7+
"fmt"
78
"reflect"
89
"time"
9-
"fmt"
1010
)
1111

1212
// NewFoo creates a new Foo with the given initial values.
@@ -64,7 +64,7 @@ func (f Foo) FilterLabels(fn func(string) bool, n int) []string {
6464
for i := range f.labels {
6565
if fn(f.labels[i]) {
6666
if result = append(result, f.labels[i]); len(result) >= cap {
67-
return result
67+
break
6868
}
6969
}
7070
}
@@ -156,7 +156,7 @@ func (b Bar) FilterTimes(fn func(time.Time) bool, n int) []time.Time {
156156
for i := range b.times {
157157
if fn(b.times[i]) {
158158
if result = append(result, b.times[i]); len(result) >= cap {
159-
return result
159+
break
160160
}
161161
}
162162
}

internal/testdata/person/person.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ type Person struct {
88
}
99

1010
type Persons struct {
11-
Ps []Person `meta:"filter,omitfield;mapper,int,omitfield;sort,stringer"`
11+
result []Person `meta:"wrapper;new;filter;mapper,int;sort,stringer;getter"`
1212
}

internal/testdata/person/person_meta.go

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,50 +13,64 @@ func (p Person) String() string {
1313
return fmt.Sprintf("%v", p.Name)
1414
}
1515

16-
// Filter returns a copy of Ps, omitting elements that are rejected by the given function.
16+
// NewPersons creates a new Persons with the given initial values.
17+
func NewPersons(result []Person) Persons {
18+
return Persons{
19+
result: result,
20+
}
21+
}
22+
23+
// Filter returns a copy of result, omitting elements that are rejected by the given function.
1724
// The n argument determines the maximum number of elements to return (n < 1: all elements).
18-
func (p Persons) Filter(fn func(Person) bool, n int) []Person {
25+
func (p Persons) Filter(fn func(Person) bool, n int) Persons {
1926
cap := n
2027
if n < 1 {
21-
cap = len(p.Ps)
28+
cap = len(p.result)
2229
}
2330
result := make([]Person, 0, cap)
24-
for i := range p.Ps {
25-
if fn(p.Ps[i]) {
26-
if result = append(result, p.Ps[i]); len(result) >= cap {
27-
return result
31+
for i := range p.result {
32+
if fn(p.result[i]) {
33+
if result = append(result, p.result[i]); len(result) >= cap {
34+
break
2835
}
2936
}
3037
}
31-
return result
38+
p.result = result
39+
return p
3240
}
3341

34-
// MapToInt returns a new slice with the results of calling the given function for each element of Ps.
42+
// MapToInt returns a new slice with the results of calling the given function for each element of result.
3543
func (p Persons) MapToInt(fn func(Person) int) []int {
36-
result := make([]int, len(p.Ps))
37-
for i := range p.Ps {
38-
result[i] = fn(p.Ps[i])
44+
result := make([]int, len(p.result))
45+
for i := range p.result {
46+
result[i] = fn(p.result[i])
3947
}
4048
return result
4149
}
4250

4351
// Len is the number of elements in the collection.
4452
func (p Persons) Len() int {
45-
return len(p.Ps)
53+
return len(p.result)
4654
}
4755

4856
// Swap swaps the elements with indexes i and j.
4957
func (p Persons) Swap(i, j int) {
50-
p.Ps[i], p.Ps[j] = p.Ps[j], p.Ps[i]
58+
p.result[i], p.result[j] = p.result[j], p.result[i]
5159
}
5260

5361
// Sort is a convenience method.
54-
func (p Persons) Sort() {
62+
func (p Persons) Sort() Persons {
5563
sort.Sort(p)
64+
return p
5665
}
5766

5867
// Less reports whether the element with
5968
// index i should sort before the element with index j.
6069
func (p Persons) Less(i, j int) bool {
61-
return p.Ps[i].String() < p.Ps[j].String()
70+
return p.result[i].String() < p.result[j].String()
71+
}
72+
73+
// Result returns the value of result.
74+
func (p Persons) Result() []Person {
75+
return p.result
6276
}

internal/testdata/person/person_meta_test.go

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,47 +8,47 @@ import (
88
)
99

1010
func Example() {
11-
persons := Persons{[]Person{
11+
ps := []Person{
1212
{
13-
Name: "Ann",
14-
Birthdate: time.Date(1983, time.February, 12, 0, 0, 0, 0, time.Local),
13+
Name: "Charlie",
14+
Birthdate: time.Date(1994, time.June, 28, 0, 0, 0, 0, time.Local),
1515
},
1616
{
1717
Name: "Bob",
1818
Birthdate: time.Date(2007, time.August, 8, 0, 0, 0, 0, time.Local),
1919
},
2020
{
21-
Name: "Charlie",
22-
Birthdate: time.Date(1994, time.June, 28, 0, 0, 0, 0, time.Local),
21+
Name: "Ann",
22+
Birthdate: time.Date(1983, time.February, 12, 0, 0, 0, 0, time.Local),
2323
},
24-
}}
24+
}
2525

2626
var name string
2727
hasName := func(p Person) bool { return p.Name == name }
2828

2929
name = "David"
30-
if found := persons.Filter(hasName, 1); len(found) > 0 {
30+
if found := NewPersons(ps).Filter(hasName, 1).Result(); len(found) > 0 {
3131
// contains David
3232
fmt.Println(found[0])
3333
}
3434

3535
name = "Bob"
36-
if found := persons.Filter(hasName, 1); len(found) > 0 {
36+
if found := NewPersons(ps).Filter(hasName, 1).Result(); len(found) > 0 {
3737
// contains Bob
3838
fmt.Println(found[0])
3939
}
4040

41-
// exludes Bob
42-
found := persons.Filter(func(p Person) bool { return p.Name != name }, -1)
43-
fmt.Println(found)
44-
45-
// map to ages
46-
ages := persons.MapToInt(func(p Person) int { return time.Now().Year() - p.Birthdate.Year() })
41+
ages := NewPersons(ps).
42+
// exclude Bob
43+
Filter(func(p Person) bool { return p.Name != name }, -1).
44+
// sort by name
45+
Sort().
46+
// map to ages
47+
MapToInt(func(p Person) int { return time.Now().Year() - p.Birthdate.Year() })
4748
fmt.Println(ages)
4849

4950
// Output: Bob
50-
// [Ann Charlie]
51-
// [36 12 25]
51+
// [36 25]
5252
}
5353

5454
func TestPersons_Sort(t *testing.T) {
@@ -59,11 +59,13 @@ func TestPersons_Sort(t *testing.T) {
5959
}{
6060
{
6161
name: "abc",
62-
p: Persons{[]Person{
63-
{Name: "b"},
64-
{Name: "c"},
65-
{Name: "a"},
66-
}},
62+
p: NewPersons(
63+
[]Person{
64+
{Name: "b"},
65+
{Name: "c"},
66+
{Name: "a"},
67+
},
68+
),
6769
want: []Person{
6870
{Name: "a"},
6971
{Name: "b"},
@@ -74,8 +76,8 @@ func TestPersons_Sort(t *testing.T) {
7476
for _, tt := range tests {
7577
t.Run(tt.name, func(t *testing.T) {
7678
tt.p.Sort()
77-
if !reflect.DeepEqual(tt.p.Ps, tt.want) {
78-
t.Errorf("got = %v, want %v", tt.p.Ps, tt.want)
79+
if !reflect.DeepEqual(tt.p.Result(), tt.want) {
80+
t.Errorf("got = %v, want %v", tt.p.Result(), tt.want)
7981
}
8082
})
8183
}

templates/bindata.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func equal_tmpl() ([]byte, error) {
3434
)
3535
}
3636

37-
var _filter_tmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\x91\x31\x6f\xa4\x30\x14\x84\x6b\x90\xf8\x0f\x53\x82\x84\xe0\xae\xdd\x3b\x4e\xba\x26\x65\x8a\xd5\x2a\x4d\x94\xc2\x0b\x0f\xd6\x89\xfd\x8c\x8c\x59\x65\x85\xf8\xef\x91\x6d\x92\xcd\x16\x9b\x06\xc9\x9e\x79\xef\x9b\xc1\x75\x8d\x65\xa9\x1e\x85\xa6\x75\x85\x25\x37\x5b\x9e\x20\xd0\x9a\xf1\x02\xd3\x7b\xed\x41\x75\x51\x2e\x61\xb4\x74\x4e\xf2\x00\x52\xa4\x89\xdd\x04\x77\x12\x0e\xc2\x12\x2c\xbd\x52\xeb\xa8\xc3\xf1\x02\x77\x22\x0c\xf2\x4c\x8c\x7e\xe6\xd6\x49\xc3\x55\x96\xd6\x35\x0e\x27\x02\x43\xd8\x61\xf6\xc3\xe8\xc8\x91\xd5\x92\x69\x0a\x13\x5a\xbc\x4b\x3d\x6b\xf0\xac\x8f\x64\x3d\xfc\x4a\x31\x5b\x34\xe4\x8c\xbf\xf8\xbd\x83\x50\xea\x4b\x2e\xaa\x2c\xf5\x20\xe4\xcb\x52\xed\xdb\xf3\x56\x26\x1e\x0e\x97\x91\xd6\xb5\xb8\x96\xcc\xfb\x18\xcb\x9b\xff\xdb\xe1\x53\x3f\x1a\xa3\x4a\x30\x24\xbb\x60\xde\x93\x7b\x12\x6a\xf2\x7b\xb2\x34\x69\xc5\x88\x5d\x03\xce\xd2\x44\xf6\x08\x19\xc2\x7d\x10\x1a\x28\xe2\x1b\x76\xf5\xfd\xb7\x15\x59\x9a\xac\x59\x9a\x58\x9a\x66\xe5\xfc\x1a\x2d\xde\x28\x8f\x96\x88\x2f\xf1\xab\x44\x2b\x46\x6f\xed\x8d\x85\xf4\x2e\x2b\x78\x20\xdc\x5d\x1b\xf9\xb2\x47\xff\x03\xfb\x59\xbe\x14\xd1\xe8\x9d\x5b\x82\x06\x62\x1c\x89\xbb\x3c\x9e\xcb\xfb\x0c\x3f\xfe\x27\xb4\x8b\xd6\x02\xff\x1a\x9f\x73\x5b\x99\x6c\x8f\x12\xc5\x70\xe5\x8b\x86\x4f\x6c\x7c\x23\xaf\x1f\x01\x00\x00\xff\xff\x33\x60\x08\x76\x6c\x02\x00\x00")
37+
var _filter_tmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\x90\x41\xef\x94\x30\x14\xc4\xcf\x90\xf0\x1d\xe6\x08\x09\x01\xbd\xfe\x15\x13\x2f\xde\xf4\xb0\x6e\xbc\x18\x0f\x05\x1e\x6c\xdd\xf6\x95\x94\xb2\x71\x43\xfa\xdd\x4d\x5b\x74\xf5\xb0\xff\x03\x84\x32\xf3\xde\x6f\xa6\x6d\x8b\x7d\x6f\xbe\x08\x4d\xde\xc3\x92\xdb\x2c\xaf\x10\x18\xcc\x72\x87\x99\x82\xf6\x49\x8d\x49\xae\x61\xb4\x74\x4e\xf2\x0c\x52\xa4\x89\xdd\x0a\x77\x11\x0e\xc2\x12\x2c\xfd\xa4\xc1\xd1\x88\xfe\x0e\x77\x21\xcc\xf2\x46\x8c\x69\xe3\xc1\x49\xc3\x4d\x91\xb7\x2d\xce\x17\x02\x43\xd8\x79\x0b\xc3\x18\xc9\x91\xd5\x92\x69\x8d\x13\x5a\xfc\x92\x7a\xd3\xe0\x4d\xf7\x64\x03\xfc\x41\x31\x47\x34\x94\x8c\xf7\x78\xfb\x02\xa1\xd4\x5f\xb9\x6a\x8a\x3c\x80\x50\xee\x7b\x73\x1a\x6e\x47\x99\x74\x38\xdf\x17\xf2\xbe\x7a\x94\x2c\xa7\x14\x2b\x98\x3f\xda\xf9\x8f\xde\x1b\xa3\x6a\x30\x24\xbb\x68\x3e\x91\xfb\x26\xd4\x1a\xf6\x14\x79\x36\x88\x05\x2f\x1d\xb8\xc8\x33\x39\x21\x66\x88\xff\xa3\xd0\x41\x11\xff\xc7\x6e\xfe\xbd\xb6\xaa\xc8\x33\x5f\xe4\x99\xa5\x75\x53\x2e\xac\xd1\xe2\x4a\x65\xb2\x24\x7c\x8d\x37\x35\x06\xb1\x04\xeb\x64\x2c\x64\x70\x59\xc1\x33\xe1\xe9\xda\xc4\x97\x13\xa6\x57\xd8\xdf\xe5\x8f\x2a\x19\x83\xf3\x48\xd0\x41\x2c\x0b\xf1\x58\xa6\x73\xfd\x9c\x11\xc6\xdf\xc5\x76\xc9\x5a\xe1\x43\x17\x72\x1e\x2b\xb3\xde\x92\xb8\xc6\xcf\x50\x30\xbe\xc2\xb3\xef\xcd\x67\xb9\x0e\xe1\x0e\xbf\x3a\xed\xbc\x2f\x72\xff\x3b\x00\x00\xff\xff\x56\x40\xed\xe4\x68\x02\x00\x00")
3838

3939
func filter_tmpl() ([]byte, error) {
4040
return bindata_read(
@@ -88,7 +88,7 @@ func setter_tmpl() ([]byte, error) {
8888
)
8989
}
9090

91-
var _sort_tmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x90\xc1\x6a\xb4\x40\x10\x84\xcf\xbf\xe0\x3b\xd4\x51\x61\xd1\x27\xf8\xaf\x39\x85\x1c\x92\xdc\x82\x07\x33\xf6\x62\xcb\xd8\x23\x33\xed\x9a\x30\xcc\xbb\x87\x51\x08\x9b\xc3\x2e\x7b\x2c\xaa\xbb\xf8\xaa\xda\x16\xcf\x24\xe0\x00\x1d\x09\xb2\xce\x9f\xe4\xe1\xce\x20\x4b\x33\x89\x06\xb0\xec\x8e\x71\xd6\x92\x51\x76\xd2\x94\xc5\x79\x15\x83\x2a\xc6\xe6\xd5\x5c\x5e\xfa\x99\x52\xc2\x21\xde\xbf\x17\x4a\xa9\xce\x91\x55\x0d\x16\x45\x2c\x8b\x7f\x9e\x74\xf5\x02\x4b\xf2\xe7\xa7\x89\xb1\x79\xb2\xc3\x21\xea\xb2\x48\x65\x51\x16\x6d\x8b\xb7\xad\x5f\x10\xb6\x7e\x39\x98\x7e\x49\x36\xd6\x11\x2c\x03\x7d\x51\x00\xa3\x97\x01\xd3\x23\x30\x39\xaf\xe2\x13\xa6\x0c\x54\xef\x44\x37\x31\x3e\xb8\x3b\xe1\xb6\x3b\x75\xf8\x7f\xd7\xbe\xf7\xcc\xdd\x55\x47\xe7\x35\x8f\xde\xc3\x38\xb9\x90\x30\x89\x21\xcc\xa4\xa3\x1b\x1e\xea\xe4\xbc\x56\x47\x97\xe0\xbc\x36\xbb\xbe\xbe\xcf\x7b\xfe\x04\x00\x00\xff\xff\xc7\x64\x49\x85\xdc\x01\x00\x00")
91+
var _sort_tmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x91\xc1\x6a\xac\x40\x10\x45\xd7\xaf\xa1\xff\xe1\x2e\x6d\x18\xf4\x0b\xde\x36\xab\x90\x45\x92\x5d\x70\x61\xda\x1a\x2c\xd1\x6a\x69\xcb\x31\x41\xfa\xdf\x83\x0a\x89\xb3\x98\x61\x96\x55\x87\x5b\x9c\xdb\x5d\x14\x78\x26\x01\x8f\xd0\x86\x20\x53\xff\x49\x11\xe1\x0c\xea\xa8\x27\xd1\x11\x2c\x1b\xf1\xa1\xeb\xc8\x2b\x07\xc9\xad\x39\x4f\xe2\x91\x2d\x4b\xfe\xea\x2f\x2f\x55\x4f\x29\x61\x1f\xde\xbf\x07\x4a\xc9\xad\x27\x33\x07\x16\xc5\x62\xcd\xbf\x48\x3a\x45\x41\x47\x72\x95\xc9\x97\x25\x7f\xea\xea\x7d\x70\xd6\x24\x6b\xac\x29\x0a\xbc\xcd\xd5\x80\x71\xae\x86\xdd\xe9\xd7\x64\x66\x6d\xc0\x52\xd3\x17\x8d\x60\x54\x52\xa3\x7d\x44\x66\xbd\x97\xf1\x09\xed\x2a\xe4\x36\xa3\x9b\x1a\x1f\x5c\x9e\x70\x9b\xb6\x25\xfe\xdf\xc5\xf7\xc2\x5c\x1e\x3a\x86\xa8\xeb\xa3\x57\xf0\x41\x2e\x24\x4c\xe2\x09\x3d\x69\x13\xea\x87\x3a\x85\xa8\x99\xbb\x5a\x6e\xc5\xc6\x10\x35\xdf\xe0\x31\xec\xfe\x3e\xe1\xb8\xb6\x26\xfd\x04\x00\x00\xff\xff\x4b\xe9\xb7\x0f\xff\x01\x00\x00")
9292

9393
func sort_tmpl() ([]byte, error) {
9494
return bindata_read(

templates/filter.tmpl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ func ({{.RcvName}} {{.RcvType}}) {{.Name}}(fn func({{.ArgType}}) bool, n int) {{
99
for i := range {{.RcvName}}.{{.FldName}} {
1010
if fn({{.RcvName}}.{{.FldName}}[i]) {
1111
if result = append(result, {{.RcvName}}.{{.FldName}}[i]); len(result) >= cap {
12-
return result
12+
break
1313
}
1414
}
1515
}
16-
return result
16+
{{.Misc.RetStmt}}
1717
}

templates/sort.tmpl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ func ({{.RcvName}} {{.RcvType}}) Swap(i, j int) {
99
}
1010

1111
// Sort is a convenience method.
12-
func ({{.RcvName}} {{.RcvType}}) Sort() {
12+
func ({{.RcvName}} {{.RcvType}}) Sort() {{.RcvType}} {
1313
sort.Sort({{.RcvName}})
14+
return {{.RcvName}}
1415
}

0 commit comments

Comments
 (0)