Go metaprogramming using struct tags + generate
go get github.com/phelmkamp/metatag
-
Define struct tags
Format is
meta:"directive[,option][;directive2]"
. For example:type Foo struct { name, Desc string `meta:"getter"` size int `meta:"ptr;getter;setter"` labels []string `meta:"setter;getter;mapper,int"` }
-
Run command
metatag --path=$SRCDIR
Better yet, add the following comment to a file at the root of your source tree (e.g. main.go) and run
go generate
as part of your build process.//go:generate metatag
-
Enjoy!
A *_meta.go file is generated for each *.go file that has meta tags. You can review/modify the generated code, write corresponding tests, etc! Just be aware that any changes will be overwritten the next time the tool runs.
getter
Generates a getter. Method name is the name of the field.
Get
is prepended to the name if and only if the field is already exported.
Uses value receiver by default.
setter
Generates a setter. Method name is the name of the field prepended with Set
.
Always uses pointer receiver.
filter
(slice only)
Generates a method that returns a copy of the slice, omitting elements that are rejected by the given function.
Method name is Filter
followed by the name of the field. Includes a Filter*N
method to support limit/contains/findFirst functionality.
Uses value receiver by default.
Options
omitfield
: exclude field name from method (i.e. justFilter
)chain
: store result in-place and return the receiver (facilitates method chaining)
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.
Uses value receiver by default.
sort
(slice only)
Generates Len
and Swap
methods to implement sort.Interface, along with a Sort
convenience method.
A Less
method can be implemented separately or generated using one of the options.
Uses value receivers by default.
Options
stringer
: generate aLess
method that compares elements by their string representationsfunc
: generate aLess
method that accepts a less function
wrapper
(slice only)
Indicates that the struct is a "wrapper" for the given slice. Enables omitfield
and chain
options for all subsequent directives.
See person.Persons for an example.
stringer
Includes the field in the result of the generated String
method. Uses value receiver by default.
new
Includes the field as an argument to the generated New$Type
method.
equal
Includes the field in the generated Equal
method.
Specify the reflect
option to compare the field using reflect.DeepEqual
.
Uses value receiver by default.
ptr
Specifies that a pointer receiver be used for all subsequent directives.
-
Why generate getters and setters?
Getters/setters are sometimes necessary to adhere to a "data contract" since Go interfaces only match methods, not fields. Getters/setters are great candidates for code generation because they are true boilerplate where names and types directly correspond to a particular field.
-
Why code generation instead of reflection?
Code generation provides compile-time type safety which is a critical feature of Go and languages like it. Plus, generation produces easy-to-understand code that you can review and modify as you see fit!
-
Why struct tags?
Struct tags are well suited for this task because they are designed to provide auxilliary information to tools/packages in a concise and unobtrusive way. Also, generating methods for a struct gives us a nice "namespace" with a low probability of collisions (as opposed to package-level functions).
-
What is a slice wrapper?
A slice wrapper is a struct that contains a slice, allowing you to define methods that operate on the slice. This is similar to the slice types in the sort package, but by using a struct we can define meta tags for the desired functionality. The methods can also return the wrapper type to support chaining.