Skip to content

Commit

Permalink
Merge pull request #32 from phelmkamp/cleanup
Browse files Browse the repository at this point in the history
Cleanup
  • Loading branch information
phelmkamp authored Dec 22, 2019
2 parents cd46cae + 563c362 commit e799214
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 69 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ Go metaprogramming using struct tags + generate

1. Define struct tags

Format is `meta:"[directive1][;directive2]"`. For example:
Format is `meta:"directive[,option][;directive2]"`. For example:
```go
type Foo struct {
name, Desc string `meta:"getter"`
size int `meta:"ptr;getter;setter"`
labels []string `meta:"setter;getter;filter"`
labels []string `meta:"setter;getter;mapper,int"`
}
```

Expand Down Expand Up @@ -56,7 +56,7 @@ Generates a method that returns a copy of the slice, omitting elements that are
Method name is `Filter`, followed by the name of the field unless `omitfield` is specified.
Uses value receiver by default.

`map:$Type` (slice only)
`mapper,$type` (slice only)

Generates a method that returns the result of mapping all elements to the specified type using the given function.
Method name is of the form `MapFieldTo$Type`, or just `MapTo$Type` if `omitfield` is specified.
Expand Down
91 changes: 66 additions & 25 deletions directive/directive.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,60 @@ const (
optReflect = "reflect"
)

// Target represents the target of the directive
var (
runFuncs = map[string]runFunc{
"ptr": ptr,
"getter": getter,
"setter": setter,
"filter": filter,
"mapper": mapper,
"sort": sort,
"stringer": stringer,
"new": runNew,
"equal": equal,
}
)

// Target represents the target of the directive.
type Target struct {
MetaFile *meta.File
RcvName, RcvType string
FldNames []string
FldType string
}

// Ptr converts the receiver to a pointer for all subsequent directives
func Ptr(tgt *Target) {
type runFunc func(*Target, []string)

// RunAll runs all of the given directives.
func RunAll(ds []string, tgt *Target) {
for i := range ds {
Run(ds[i], tgt)
}
}

// Run runs the given directive.
func Run(d string, tgt *Target) {
opts := strings.Split(d, ",")
d = opts[0]
opts = opts[1:]

run, ok := runFuncs[d]
if !ok {
log.Printf("Unknown directive: %s\n", d)
return
}

run(tgt, opts)
}

// ptr converts the receiver to a pointer for all subsequent directives.
func ptr(tgt *Target, opts []string) {
tgt.RcvType = "*" + tgt.RcvType
log.Printf("Using pointer receiver: %s\n", tgt.RcvType)
}

// Getter generates a getter method for each name of the given field
func Getter(tgt *Target) {
// getter generates a getter method for each name of the given field.
func getter(tgt *Target, opts []string) {
for _, fldNm := range tgt.FldNames {
method := upperFirst(fldNm)
if method == fldNm {
Expand All @@ -50,8 +88,8 @@ func Getter(tgt *Target) {
}
}

// Setter generates a setter method for each name of the given field
func Setter(tgt *Target) {
// setter generates a setter method for each name of the given field.
func setter(tgt *Target, opts []string) {
elemType := strings.TrimPrefix(tgt.FldType, "[]")

arg := argName(tgt.RcvName, elemType)
Expand All @@ -78,8 +116,8 @@ func Setter(tgt *Target) {
}
}

// Filter generates a filter method for each name of the given field
func Filter(tgt *Target, opts []string) {
// filter generates a filter method for each name of the given field.
func filter(tgt *Target, opts []string) {
elemType := strings.TrimPrefix(tgt.FldType, "[]")

var isOmitField bool
Expand Down Expand Up @@ -112,15 +150,23 @@ func Filter(tgt *Target, opts []string) {
}
}

// Map generates a mapper method for each name of the given field
func Map(tgt *Target, result string, opts []string) {
elemType := strings.TrimPrefix(tgt.FldType, "[]")
// mapper generates a mapper method for each name of the given field.
func mapper(tgt *Target, opts []string) {
if len(opts) < 1 {
log.Print("skipping 'mapper' - must specify target type as first option\n")
return
}

result := opts[0]
opts = opts[1:]

sel := result
if resSubs := strings.SplitN(result, ".", 2); len(resSubs) > 1 {
sel = resSubs[1]
}

elemType := strings.TrimPrefix(tgt.FldType, "[]")

var isOmitField bool
for i := range opts {
if opts[i] == optOmitField {
Expand All @@ -130,11 +176,6 @@ func Map(tgt *Target, result string, opts []string) {
}

for _, fldNm := range tgt.FldNames {
if elemType == tgt.FldType {
log.Printf("'map' not valid for field %s.%s - must be a slice\n", tgt.RcvName, fldNm)
continue
}

var fldPart string
if !isOmitField {
fldPart = upperFirst(fldNm)
Expand All @@ -155,8 +196,8 @@ func Map(tgt *Target, result string, opts []string) {
}
}

// Sort generates sort methods for the first name of the given field
func Sort(tgt *Target, opts []string) {
// sort generates sort methods for the first name of the given field.
func sort(tgt *Target, opts []string) {
log.Print("Adding import: \"sort\"\n")
tgt.MetaFile.Imports["sort"] = struct{}{}

Expand Down Expand Up @@ -192,8 +233,8 @@ func Sort(tgt *Target, opts []string) {
}
}

// Stringer adds each name of the given field to the String() implementation
func Stringer(tgt *Target) {
// stringer adds each name of the given field to the String() implementation.
func stringer(tgt *Target, opts []string) {
log.Print("Adding import: \"fmt\"\n")
tgt.MetaFile.Imports["fmt"] = struct{}{}

Expand Down Expand Up @@ -227,8 +268,8 @@ func Stringer(tgt *Target) {
}
}

// New adds each name of the given field to the New() implementation
func New(tgt *Target) {
// runNew adds each name of the given field to the New() implementation.
func runNew(tgt *Target, opts []string) {
method := "New" + upperFirst(tgt.RcvType)
for _, fldNm := range tgt.FldNames {
log.Printf("Adding to method: %s\n", method)
Expand Down Expand Up @@ -256,8 +297,8 @@ func New(tgt *Target) {
}
}

// Equal adds each name of the given field to the Equal() implementation
func Equal(tgt *Target, opts []string) {
// equal adds each name of the given field to the Equal() implementation.
func equal(tgt *Target, opts []string) {
for _, fldNm := range tgt.FldNames {
log.Print("Adding to method: Equal\n")
found := tgt.MetaFile.FilterMethods(
Expand Down
6 changes: 3 additions & 3 deletions internal/testdata/foobar/foo.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ type Foo struct {
NoMetaJSON string `json:"omitempty"`
name, Desc string `meta:"new;getter;stringer"`
size int `meta:"stringer;ptr;getter;setter"`
labels []string `meta:"new;setter;getter;filter;map:time.Time"`
labels []string `meta:"new;setter;getter;filter;mapper,time.Time"`
stringer fmt.Stringer `meta:"setter"`
}

type Bar struct {
name string `meta:"stringer;equal"`
foos []Foo `meta:"getter;setter;map:string"`
foos []Foo `meta:"getter;setter;mapper,string"`
pairs map[string]float64 `meta:"getter;setter"`
times []time.Time `meta:"getter;setter;filter;map:int64;equal,reflect"`
times []time.Time `meta:"getter;setter;filter;mapper,int64;equal,reflect"`
baz bool `meta:"setter"`
}
2 changes: 1 addition & 1 deletion internal/testdata/person/person.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ type Person struct {
}

type Persons struct {
Ps []Person `meta:"filter,omitfield;map:int,omitfield;sort,stringer"`
Ps []Person `meta:"filter,omitfield;mapper,int,omitfield;sort,stringer"`
}
38 changes: 1 addition & 37 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,43 +149,7 @@ func main() {
fldTgt.FldNames[i] = f.Names[i].Name
}

directives := strings.Split(metaTag, ";")
for _, d := range directives {
dSubs := strings.SplitN(d, ":", 2)
if len(dSubs) < 1 {
continue
}

opts := strings.Split(dSubs[len(dSubs)-1], ",")
dSubs[len(dSubs)-1] = opts[0]
opts = opts[1:]

switch dSubs[0] {
case "ptr":
directive.Ptr(&fldTgt)
case "getter":
directive.Getter(&fldTgt)
case "setter":
directive.Setter(&fldTgt)
case "filter":
directive.Filter(&fldTgt, opts)
case "map":
if len(dSubs) < 2 {
continue
}
directive.Map(&fldTgt, dSubs[1], opts)
case "sort":
directive.Sort(&fldTgt, opts)
case "stringer":
directive.Stringer(&fldTgt)
case "new":
directive.New(&fldTgt)
case "equal":
directive.Equal(&fldTgt, opts)
default:
log.Printf("Unknown directive: %s\n", d)
}
}
directive.RunAll(strings.Split(metaTag, ";"), &fldTgt)

if fldPkg != "" {
var importPath string
Expand Down

0 comments on commit e799214

Please sign in to comment.