• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

examples/H30-Aug-2018-

generic/H30-Aug-2018-

out/H30-Aug-2018-

parse/H30-Aug-2018-

.gitignoreH A D30-Aug-2018273

.travis.ymlH A D30-Aug-201842

LICENSEH A D30-Aug-20181.1 KiB

README.mdH A D30-Aug-20187.3 KiB

doc.goH A D30-Aug-201865

main.goH A D30-Aug-20183.2 KiB

main_test.goH A D30-Aug-201818

README.md

1# genny - Generics for Go
2
3[![Build Status](https://travis-ci.org/cheekybits/genny.svg?branch=master)](https://travis-ci.org/cheekybits/genny) [![GoDoc](https://godoc.org/github.com/cheekybits/genny/parse?status.png)](http://godoc.org/github.com/cheekybits/genny/parse)
4
5Install:
6
7```
8go get github.com/cheekybits/genny
9```
10
11=====
12
13(pron. Jenny) by Mat Ryer ([@matryer](https://twitter.com/matryer)) and Tyler Bunnell ([@TylerJBunnell](https://twitter.com/TylerJBunnell)).
14
15Until the Go core team include support for [generics in Go](http://golang.org/doc/faq#generics), `genny` is a code-generation generics solution. It allows you write normal buildable and testable Go code which, when processed by the `genny gen` tool, will replace the generics with specific types.
16
17  * Generic code is valid Go code
18  * Generic code compiles and can be tested
19  * Use `stdin` and `stdout` or specify in and out files
20  * Supports Go 1.4's [go generate](http://tip.golang.org/doc/go1.4#gogenerate)
21  * Multiple specific types will generate every permutation
22  * Use `BUILTINS` and `NUMBERS` wildtype to generate specific code for all built-in (and number) Go types
23  * Function names and comments also get updated
24
25## Library
26
27We have started building a [library of common things](https://github.com/cheekybits/gennylib), and you can use `genny get` to generate the specific versions you need.
28
29For example: `genny get maps/concurrentmap.go "KeyType=BUILTINS ValueType=BUILTINS"` will print out generated code for all types for a concurrent map. Any file in the library may be generated locally in this way using all the same options given to `genny gen`.
30
31## Usage
32
33```
34genny [{flags}] gen "{types}"
35
36gen - generates type specific code from generic code.
37get <package/file> - fetch a generic template from the online library and gen it.
38
39{flags}  - (optional) Command line flags (see below)
40{types}  - (required) Specific types for each generic type in the source
41{types} format:  {generic}={specific}[,another][ {generic2}={specific2}]
42
43Examples:
44  Generic=Specific
45  Generic1=Specific1 Generic2=Specific2
46  Generic1=Specific1,Specific2 Generic2=Specific3,Specific4
47
48Flags:
49  -in="": file to parse instead of stdin
50  -out="": file to save output to instead of stdout
51  -pkg="": package name for generated files
52```
53
54  * Comma separated type lists will generate code for each type
55
56### Flags
57
58  * `-in` - specify the input file (rather than using stdin)
59  * `-out` - specify the output file (rather than using stdout)
60
61### go generate
62
63To use Go 1.4's `go generate` capability, insert the following comment in your source code file:
64
65```
66//go:generate genny -in=$GOFILE -out=gen-$GOFILE gen "KeyType=string,int ValueType=string,int"
67```
68
69  * Start the line with `//go:generate `
70  * Use the `-in` and `-out` flags to specify the files to work on
71  * Use the `genny` command as usual after the flags
72
73Now, running `go generate` (in a shell) for the package will cause the generic versions of the files to be generated.
74
75  * The output file will be overwritten, so it's safe to call `go generate` many times
76  * Use `$GOFILE` to refer to the current file
77  * The `//go:generate` line will be removed from the output
78
79To see a real example of how to use `genny` with `go generate`, look in the [example/go-generate directory](https://github.com/cheekybits/genny/tree/master/examples/go-generate).
80
81## How it works
82
83Define your generic types using the special `generic.Type` placeholder type:
84
85```go
86type KeyType generic.Type
87type ValueType generic.Type
88```
89
90  * You can use as many as you like
91  * Give them meaningful names
92
93Then write the generic code referencing the types as your normally would:
94
95```go
96func SetValueTypeForKeyType(key KeyType, value ValueType) { /* ... */ }
97```
98
99  * Generic type names will also be replaced in comments and function names (see Real example below)
100
101Since `generic.Type` is a real Go type, your code will compile, and you can even write unit tests against your generic code.
102
103#### Generating specific versions
104
105Pass the file through the `genny gen` tool with the specific types as the argument:
106
107```
108cat generic.go | genny gen "KeyType=string ValueType=interface{}"
109```
110
111The output will be the complete Go source file with the generic types replaced with the types specified in the arguments.
112
113## Real example
114
115Given [this generic Go code](https://github.com/cheekybits/genny/tree/master/examples/queue) which compiles and is tested:
116
117```go
118package queue
119
120import "github.com/cheekybits/genny/generic"
121
122// NOTE: this is how easy it is to define a generic type
123type Something generic.Type
124
125// SomethingQueue is a queue of Somethings.
126type SomethingQueue struct {
127  items []Something
128}
129
130func NewSomethingQueue() *SomethingQueue {
131  return &SomethingQueue{items: make([]Something, 0)}
132}
133func (q *SomethingQueue) Push(item Something) {
134  q.items = append(q.items, item)
135}
136func (q *SomethingQueue) Pop() Something {
137  item := q.items[0]
138  q.items = q.items[1:]
139  return item
140}
141```
142
143When `genny gen` is invoked like this:
144
145```
146cat source.go | genny gen "Something=string"
147```
148
149It outputs:
150
151```go
152// This file was automatically generated by genny.
153// Any changes will be lost if this file is regenerated.
154// see https://github.com/cheekybits/genny
155
156package queue
157
158// StringQueue is a queue of Strings.
159type StringQueue struct {
160  items []string
161}
162
163func NewStringQueue() *StringQueue {
164  return &StringQueue{items: make([]string, 0)}
165}
166func (q *StringQueue) Push(item string) {
167  q.items = append(q.items, item)
168}
169func (q *StringQueue) Pop() string {
170  item := q.items[0]
171  q.items = q.items[1:]
172  return item
173}
174```
175
176To get a _something_ for every built-in Go type plus one of your own types, you could run:
177
178```
179cat source.go | genny gen "Something=BUILTINS,*MyType"
180```
181
182#### More examples
183
184Check out the [test code files](https://github.com/cheekybits/genny/tree/master/parse/test) for more real examples.
185
186## Writing test code
187
188Once you have defined a generic type with some code worth testing:
189
190```go
191package slice
192
193import (
194  "log"
195  "reflect"
196
197  "github.com/stretchr/gogen/generic"
198)
199
200type MyType generic.Type
201
202func EnsureMyTypeSlice(objectOrSlice interface{}) []MyType {
203  log.Printf("%v", reflect.TypeOf(objectOrSlice))
204  switch obj := objectOrSlice.(type) {
205  case []MyType:
206    log.Println("  returning it untouched")
207    return obj
208  case MyType:
209    log.Println("  wrapping in slice")
210    return []MyType{obj}
211  default:
212    panic("ensure slice needs MyType or []MyType")
213  }
214}
215```
216
217You can treat it like any normal Go type in your test code:
218
219```go
220func TestEnsureMyTypeSlice(t *testing.T) {
221
222  myType := new(MyType)
223  slice := EnsureMyTypeSlice(myType)
224  if assert.NotNil(t, slice) {
225    assert.Equal(t, slice[0], myType)
226  }
227
228  slice = EnsureMyTypeSlice(slice)
229  log.Printf("%#v", slice[0])
230  if assert.NotNil(t, slice) {
231    assert.Equal(t, slice[0], myType)
232  }
233
234}
235```
236
237### Understanding what `generic.Type` is
238
239Because `generic.Type` is an empty interface type (literally `interface{}`) every other type will be considered to be a `generic.Type` if you are switching on the type of an object. Of course, once the specific versions are generated, this issue goes away but it's worth knowing when you are writing your tests against generic code.
240
241### Contributions
242
243  * See the [API documentation for the parse package](http://godoc.org/github.com/cheekybits/genny/parse)
244  * Please do TDD
245  * All input welcome
246