pax_global_header00006660000000000000000000000064133420035220014504gustar00rootroot0000000000000052 comment=d2cf3cdd35ce0d789056c4bc02a4d6349c947caf genny-1.0.0/000077500000000000000000000000001334200352200126225ustar00rootroot00000000000000genny-1.0.0/.gitignore000066400000000000000000000004211334200352200146070ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof genny genny-1.0.0/.travis.yml000066400000000000000000000000521334200352200147300ustar00rootroot00000000000000language: go go: - 1.7 - 1.8 - 1.9 genny-1.0.0/LICENSE000066400000000000000000000020661334200352200136330ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014 cheekybits Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. genny-1.0.0/README.md000066400000000000000000000164231334200352200141070ustar00rootroot00000000000000# genny - Generics for Go [![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) Install: ``` go get github.com/cheekybits/genny ``` ===== (pron. Jenny) by Mat Ryer ([@matryer](https://twitter.com/matryer)) and Tyler Bunnell ([@TylerJBunnell](https://twitter.com/TylerJBunnell)). Until 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. * Generic code is valid Go code * Generic code compiles and can be tested * Use `stdin` and `stdout` or specify in and out files * Supports Go 1.4's [go generate](http://tip.golang.org/doc/go1.4#gogenerate) * Multiple specific types will generate every permutation * Use `BUILTINS` and `NUMBERS` wildtype to generate specific code for all built-in (and number) Go types * Function names and comments also get updated ## Library We 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. For 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`. ## Usage ``` genny [{flags}] gen "{types}" gen - generates type specific code from generic code. get - fetch a generic template from the online library and gen it. {flags} - (optional) Command line flags (see below) {types} - (required) Specific types for each generic type in the source {types} format: {generic}={specific}[,another][ {generic2}={specific2}] Examples: Generic=Specific Generic1=Specific1 Generic2=Specific2 Generic1=Specific1,Specific2 Generic2=Specific3,Specific4 Flags: -in="": file to parse instead of stdin -out="": file to save output to instead of stdout -pkg="": package name for generated files ``` * Comma separated type lists will generate code for each type ### Flags * `-in` - specify the input file (rather than using stdin) * `-out` - specify the output file (rather than using stdout) ### go generate To use Go 1.4's `go generate` capability, insert the following comment in your source code file: ``` //go:generate genny -in=$GOFILE -out=gen-$GOFILE gen "KeyType=string,int ValueType=string,int" ``` * Start the line with `//go:generate ` * Use the `-in` and `-out` flags to specify the files to work on * Use the `genny` command as usual after the flags Now, running `go generate` (in a shell) for the package will cause the generic versions of the files to be generated. * The output file will be overwritten, so it's safe to call `go generate` many times * Use `$GOFILE` to refer to the current file * The `//go:generate` line will be removed from the output To 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). ## How it works Define your generic types using the special `generic.Type` placeholder type: ```go type KeyType generic.Type type ValueType generic.Type ``` * You can use as many as you like * Give them meaningful names Then write the generic code referencing the types as your normally would: ```go func SetValueTypeForKeyType(key KeyType, value ValueType) { /* ... */ } ``` * Generic type names will also be replaced in comments and function names (see Real example below) Since `generic.Type` is a real Go type, your code will compile, and you can even write unit tests against your generic code. #### Generating specific versions Pass the file through the `genny gen` tool with the specific types as the argument: ``` cat generic.go | genny gen "KeyType=string ValueType=interface{}" ``` The output will be the complete Go source file with the generic types replaced with the types specified in the arguments. ## Real example Given [this generic Go code](https://github.com/cheekybits/genny/tree/master/examples/queue) which compiles and is tested: ```go package queue import "github.com/cheekybits/genny/generic" // NOTE: this is how easy it is to define a generic type type Something generic.Type // SomethingQueue is a queue of Somethings. type SomethingQueue struct { items []Something } func NewSomethingQueue() *SomethingQueue { return &SomethingQueue{items: make([]Something, 0)} } func (q *SomethingQueue) Push(item Something) { q.items = append(q.items, item) } func (q *SomethingQueue) Pop() Something { item := q.items[0] q.items = q.items[1:] return item } ``` When `genny gen` is invoked like this: ``` cat source.go | genny gen "Something=string" ``` It outputs: ```go // This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package queue // StringQueue is a queue of Strings. type StringQueue struct { items []string } func NewStringQueue() *StringQueue { return &StringQueue{items: make([]string, 0)} } func (q *StringQueue) Push(item string) { q.items = append(q.items, item) } func (q *StringQueue) Pop() string { item := q.items[0] q.items = q.items[1:] return item } ``` To get a _something_ for every built-in Go type plus one of your own types, you could run: ``` cat source.go | genny gen "Something=BUILTINS,*MyType" ``` #### More examples Check out the [test code files](https://github.com/cheekybits/genny/tree/master/parse/test) for more real examples. ## Writing test code Once you have defined a generic type with some code worth testing: ```go package slice import ( "log" "reflect" "github.com/stretchr/gogen/generic" ) type MyType generic.Type func EnsureMyTypeSlice(objectOrSlice interface{}) []MyType { log.Printf("%v", reflect.TypeOf(objectOrSlice)) switch obj := objectOrSlice.(type) { case []MyType: log.Println(" returning it untouched") return obj case MyType: log.Println(" wrapping in slice") return []MyType{obj} default: panic("ensure slice needs MyType or []MyType") } } ``` You can treat it like any normal Go type in your test code: ```go func TestEnsureMyTypeSlice(t *testing.T) { myType := new(MyType) slice := EnsureMyTypeSlice(myType) if assert.NotNil(t, slice) { assert.Equal(t, slice[0], myType) } slice = EnsureMyTypeSlice(slice) log.Printf("%#v", slice[0]) if assert.NotNil(t, slice) { assert.Equal(t, slice[0], myType) } } ``` ### Understanding what `generic.Type` is Because `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. ### Contributions * See the [API documentation for the parse package](http://godoc.org/github.com/cheekybits/genny/parse) * Please do TDD * All input welcome genny-1.0.0/doc.go000066400000000000000000000001011334200352200137060ustar00rootroot00000000000000// Package main is the command line tool for Genny. package main genny-1.0.0/examples/000077500000000000000000000000001334200352200144405ustar00rootroot00000000000000genny-1.0.0/examples/davechaney/000077500000000000000000000000001334200352200165475ustar00rootroot00000000000000genny-1.0.0/examples/davechaney/func_thing.go000066400000000000000000000003511334200352200212210ustar00rootroot00000000000000package math import "github.com/cheekybits/genny/generic" type ThisNumberType generic.Number func ThisNumberTypeMax(fn func(a, b ThisNumberType) bool, a, b ThisNumberType) ThisNumberType { if fn(a, b) { return a } return b } genny-1.0.0/examples/davechaney/gen.sh000077500000000000000000000002641334200352200176610ustar00rootroot00000000000000#!/bin/bash cat ./generic_max.go | ../../genny gen "NumberType=NUMBERS" > numbers_max_get.go cat ./func_thing.go | ../../genny gen "ThisNumberType=NUMBERS" > numbers_func_thing.go genny-1.0.0/examples/davechaney/generic_max.go000066400000000000000000000003671334200352200213650ustar00rootroot00000000000000package math import "github.com/cheekybits/genny/generic" type NumberType generic.Number // NumberTypeMax gets the maximum number from the // two specified. func NumberTypeMax(a, b NumberType) NumberType { if a > b { return a } return b } genny-1.0.0/examples/davechaney/max_test.go000066400000000000000000000006441334200352200207260ustar00rootroot00000000000000package math_test import ( "testing" "github.com/cheekybits/genny/examples/davechaney" ) func TestNumberTypeMax(t *testing.T) { var v math.NumberType v = math.NumberTypeMax(10, 20) if v != 20 { t.Errorf("Max of 10 and 20 is 20") } v = math.NumberTypeMax(20, 20) if v != 20 { t.Errorf("Max of 20 and 20 is 20") } v = math.NumberTypeMax(25, 20) if v != 25 { t.Errorf("Max of 25 and 20 is 25") } } genny-1.0.0/examples/davechaney/numbers_func_thing.go000066400000000000000000000025711334200352200227620ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package math func Float32Max(fn func(a, b float32) bool, a, b float32) float32 { if fn(a, b) { return a } return b } func Float64Max(fn func(a, b float64) bool, a, b float64) float64 { if fn(a, b) { return a } return b } func IntMax(fn func(a, b int) bool, a, b int) int { if fn(a, b) { return a } return b } func Int16Max(fn func(a, b int16) bool, a, b int16) int16 { if fn(a, b) { return a } return b } func Int32Max(fn func(a, b int32) bool, a, b int32) int32 { if fn(a, b) { return a } return b } func Int64Max(fn func(a, b int64) bool, a, b int64) int64 { if fn(a, b) { return a } return b } func Int8Max(fn func(a, b int8) bool, a, b int8) int8 { if fn(a, b) { return a } return b } func UintMax(fn func(a, b uint) bool, a, b uint) uint { if fn(a, b) { return a } return b } func Uint16Max(fn func(a, b uint16) bool, a, b uint16) uint16 { if fn(a, b) { return a } return b } func Uint32Max(fn func(a, b uint32) bool, a, b uint32) uint32 { if fn(a, b) { return a } return b } func Uint64Max(fn func(a, b uint64) bool, a, b uint64) uint64 { if fn(a, b) { return a } return b } func Uint8Max(fn func(a, b uint8) bool, a, b uint8) uint8 { if fn(a, b) { return a } return b } genny-1.0.0/examples/go-generate/000077500000000000000000000000001334200352200166355ustar00rootroot00000000000000genny-1.0.0/examples/go-generate/gen-go-generate.go000066400000000000000000000011231334200352200221250ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package gogenerate type StringStringMap map[string]string func NewStringStringMap() map[string]string { return make(map[string]string) } type StringIntMap map[string]int func NewStringIntMap() map[string]int { return make(map[string]int) } type IntStringMap map[int]string func NewIntStringMap() map[int]string { return make(map[int]string) } type IntIntMap map[int]int func NewIntIntMap() map[int]int { return make(map[int]int) } genny-1.0.0/examples/go-generate/go-generate.go000066400000000000000000000005451334200352200213650ustar00rootroot00000000000000package gogenerate import "github.com/cheekybits/genny/generic" //go:generate genny -in=$GOFILE -out=gen-$GOFILE gen "KeyType=string,int ValueType=string,int" type KeyType generic.Type type ValueType generic.Type type KeyTypeValueTypeMap map[KeyType]ValueType func NewKeyTypeValueTypeMap() map[KeyType]ValueType { return make(map[KeyType]ValueType) } genny-1.0.0/examples/queue/000077500000000000000000000000001334200352200155645ustar00rootroot00000000000000genny-1.0.0/examples/queue/build.sh000077500000000000000000000001411334200352200172160ustar00rootroot00000000000000#!/bin/bash cat ./queue_generic.go | ../../genny gen "Generic=string,int" > queue_generic_gen.go genny-1.0.0/examples/queue/queue_generic.go000066400000000000000000000013431334200352200207340ustar00rootroot00000000000000package example import "github.com/cheekybits/genny/generic" type Generic generic.Type // GenericQueue represents a queue of Generic types. type GenericQueue struct { items []Generic } // NewGenericQueue makes a new empty Generic queue. func NewGenericQueue() *GenericQueue { return &GenericQueue{items: make([]Generic, 0)} } // Enq adds an item to the queue. func (q *GenericQueue) Enq(obj Generic) *GenericQueue { q.items = append(q.items, obj) return q } // Deq removes and returns the next item in the queue. func (q *GenericQueue) Deq() Generic { obj := q.items[0] q.items = q.items[1:] return obj } // Len gets the current number of Generic items in the queue. func (q *GenericQueue) Len() int { return len(q.items) } genny-1.0.0/examples/queue/queue_generic_test.go000066400000000000000000000011151334200352200217700ustar00rootroot00000000000000package example import ( "testing" "github.com/stretchr/testify/assert" ) func TestNew(t *testing.T) { q := NewGenericQueue() assert.NotNil(t, q) } func TestEnqueueAndDequeue(t *testing.T) { item1 := new(Generic) item2 := new(Generic) q := NewGenericQueue() assert.Equal(t, q, q.Enq(item1), "Enq should return the queue") assert.Equal(t, 1, q.Len()) assert.Equal(t, q, q.Enq(item2), "Enq should return the queue") assert.Equal(t, 2, q.Len()) assert.Equal(t, item1, q.Deq()) assert.Equal(t, 1, q.Len()) assert.Equal(t, item2, q.Deq()) assert.Equal(t, 0, q.Len()) } genny-1.0.0/generic/000077500000000000000000000000001334200352200142365ustar00rootroot00000000000000genny-1.0.0/generic/doc.go000066400000000000000000000001061334200352200153270ustar00rootroot00000000000000// Package generic contains the generic marker types. package generic genny-1.0.0/generic/generic.go000066400000000000000000000007561334200352200162110ustar00rootroot00000000000000package generic // Type is the placeholder type that indicates a generic value. // When genny is executed, variables of this type will be replaced with // references to the specific types. // var GenericType generic.Type type Type interface{} // Number is the placehoder type that indiccates a generic numerical value. // When genny is executed, variables of this type will be replaced with // references to the specific types. // var GenericType generic.Number type Number float64 genny-1.0.0/main.go000066400000000000000000000063351334200352200141040ustar00rootroot00000000000000package main import ( "bytes" "flag" "fmt" "io" "io/ioutil" "net/http" "os" "strings" "github.com/cheekybits/genny/out" "github.com/cheekybits/genny/parse" ) /* source | genny gen [-in=""] [-out=""] [-pkg=""] "KeyType=string,int ValueType=string,int" */ const ( _ = iota exitcodeInvalidArgs exitcodeInvalidTypeSet exitcodeStdinFailed exitcodeGenFailed exitcodeGetFailed exitcodeSourceFileInvalid exitcodeDestFileFailed ) func main() { var ( in = flag.String("in", "", "file to parse instead of stdin") out = flag.String("out", "", "file to save output to instead of stdout") pkgName = flag.String("pkg", "", "package name for generated files") prefix = "https://github.com/metabition/gennylib/raw/master/" ) flag.Parse() args := flag.Args() if len(args) < 2 { usage() os.Exit(exitcodeInvalidArgs) } if strings.ToLower(args[0]) != "gen" && strings.ToLower(args[0]) != "get" { usage() os.Exit(exitcodeInvalidArgs) } // parse the typesets var setsArg = args[1] if strings.ToLower(args[0]) == "get" { setsArg = args[2] } typeSets, err := parse.TypeSet(setsArg) if err != nil { fatal(exitcodeInvalidTypeSet, err) } outWriter := newWriter(*out) if strings.ToLower(args[0]) == "get" { if len(args) != 3 { fmt.Println("not enough arguments to get") usage() os.Exit(exitcodeInvalidArgs) } r, err := http.Get(prefix + args[1]) if err != nil { fatal(exitcodeGetFailed, err) } b, err := ioutil.ReadAll(r.Body) if err != nil { fatal(exitcodeGetFailed, err) } r.Body.Close() br := bytes.NewReader(b) err = gen(*in, *pkgName, br, typeSets, outWriter) } else if len(*in) > 0 { var file *os.File file, err = os.Open(*in) if err != nil { fatal(exitcodeSourceFileInvalid, err) } defer file.Close() err = gen(*in, *pkgName, file, typeSets, outWriter) } else { var source []byte source, err = ioutil.ReadAll(os.Stdin) if err != nil { fatal(exitcodeStdinFailed, err) } reader := bytes.NewReader(source) err = gen("stdin", *pkgName, reader, typeSets, outWriter) } // do the work if err != nil { fatal(exitcodeGenFailed, err) } } func usage() { fmt.Fprintln(os.Stderr, `usage: genny [{flags}] gen "{types}" gen - generates type specific code from generic code. get - fetch a generic template from the online library and gen it. {flags} - (optional) Command line flags (see below) {types} - (required) Specific types for each generic type in the source {types} format: {generic}={specific}[,another][ {generic2}={specific2}] Examples: Generic=Specific Generic1=Specific1 Generic2=Specific2 Generic1=Specific1,Specific2 Generic2=Specific3,Specific4 Flags:`) flag.PrintDefaults() } func newWriter(fileName string) io.Writer { if fileName == "" { return os.Stdout } lf := &out.LazyFile{FileName: fileName} defer lf.Close() return lf } func fatal(code int, a ...interface{}) { fmt.Println(a...) os.Exit(code) } // gen performs the generic generation. func gen(filename, pkgName string, in io.ReadSeeker, typesets []map[string]string, out io.Writer) error { var output []byte var err error output, err = parse.Generics(filename, pkgName, in, typesets) if err != nil { return err } out.Write(output) return nil } genny-1.0.0/main_test.go000066400000000000000000000000221334200352200151260ustar00rootroot00000000000000package main_test genny-1.0.0/out/000077500000000000000000000000001334200352200134315ustar00rootroot00000000000000genny-1.0.0/out/lazy_file.go000066400000000000000000000016141334200352200157400ustar00rootroot00000000000000package out import ( "os" "path" ) // LazyFile is an io.WriteCloser which defers creation of the file it is supposed to write in // till the first call to its write function in order to prevent creation of file, if no write // is supposed to happen. type LazyFile struct { // FileName is path to the file to which genny will write. FileName string file *os.File } // Close closes the file if it is created. Returns nil if no file is created. func (lw *LazyFile) Close() error { if lw.file != nil { return lw.file.Close() } return nil } // Write writes to the specified file and creates the file first time it is called. func (lw *LazyFile) Write(p []byte) (int, error) { if lw.file == nil { err := os.MkdirAll(path.Dir(lw.FileName), 0755) if err != nil { return 0, err } lw.file, err = os.Create(lw.FileName) if err != nil { return 0, err } } return lw.file.Write(p) } genny-1.0.0/out/lazy_file_test.go000066400000000000000000000020021334200352200167670ustar00rootroot00000000000000package out_test import ( "github.com/cheekybits/genny/out" "github.com/stretchr/testify/assert" "io/ioutil" "os" "testing" ) const testFileName = "test-file.go" func tearDown() { var err = os.Remove(testFileName) if err != nil && !os.IsNotExist(err) { panic("Could not delete test file") } } func assertFileContains(t *testing.T, expected string) { file, err := os.Open(testFileName) if err != nil { panic(err) } fileBytes, err := ioutil.ReadAll(file) if err != nil { panic(err) } assert.Equal(t, expected, string(fileBytes), "File contents not written properly") } func TestMultipleWrites(t *testing.T) { defer tearDown() lf := out.LazyFile{FileName: testFileName} defer lf.Close() lf.Write([]byte("Word1")) lf.Write([]byte("Word2")) assertFileContains(t, "Word1Word2") } func TestNoWrite(t *testing.T) { defer tearDown() lf := out.LazyFile{FileName: testFileName} defer lf.Close() _, err := os.Stat(testFileName) assert.True(t, os.IsNotExist(err), "Expected file not to be created") } genny-1.0.0/parse/000077500000000000000000000000001334200352200137345ustar00rootroot00000000000000genny-1.0.0/parse/builtins.go000066400000000000000000000010051334200352200161100ustar00rootroot00000000000000package parse // Builtins contains a slice of all built-in Go types. var Builtins = []string{ "bool", "byte", "complex128", "complex64", "error", "float32", "float64", "int", "int16", "int32", "int64", "int8", "rune", "string", "uint", "uint16", "uint32", "uint64", "uint8", "uintptr", } // Numbers contains a slice of all built-in number types. var Numbers = []string{ "float32", "float64", "int", "int16", "int32", "int64", "int8", "uint", "uint16", "uint32", "uint64", "uint8", } genny-1.0.0/parse/doc.go000066400000000000000000000010331334200352200150250ustar00rootroot00000000000000// Package parse contains the generic code generation capabilities // that power genny. // // genny gen "{types}" // // gen - generates type specific code (to stdout) from generic code (via stdin) // // {types} - (required) Specific types for each generic type in the source // {types} format: {generic}={specific}[,another][ {generic2}={specific2}] // Examples: // Generic=Specific // Generic1=Specific1 Generic2=Specific2 // Generic1=Specific1,Specific2 Generic2=Specific3,Specific4 package parse genny-1.0.0/parse/errors.go000066400000000000000000000022351334200352200156010ustar00rootroot00000000000000package parse import ( "errors" ) // errMissingSpecificType represents an error when a generic type is not // satisfied by a specific type. type errMissingSpecificType struct { GenericType string } // Error gets a human readable string describing this error. func (e errMissingSpecificType) Error() string { return "Missing specific type for '" + e.GenericType + "' generic type" } // errImports represents an error from goimports. type errImports struct { Err error } // Error gets a human readable string describing this error. func (e errImports) Error() string { return "Failed to goimports the generated code: " + e.Err.Error() } // errSource represents an error with the source file. type errSource struct { Err error } // Error gets a human readable string describing this error. func (e errSource) Error() string { return "Failed to parse source file: " + e.Err.Error() } type errBadTypeArgs struct { Message string Arg string } func (e errBadTypeArgs) Error() string { return "\"" + e.Arg + "\" is bad: " + e.Message } var errMissingTypeInformation = errors.New("No type arguments were specified and no \"// +gogen\" tag was found in the source.") genny-1.0.0/parse/parse.go000066400000000000000000000152161334200352200154020ustar00rootroot00000000000000package parse import ( "bufio" "bytes" "fmt" "go/ast" "go/parser" "go/scanner" "go/token" "io" "os" "strings" "unicode" "golang.org/x/tools/imports" ) var header = []byte(` // This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny `) var ( packageKeyword = []byte("package") importKeyword = []byte("import") openBrace = []byte("(") closeBrace = []byte(")") genericPackage = "generic" genericType = "generic.Type" genericNumber = "generic.Number" linefeed = "\r\n" ) var unwantedLinePrefixes = [][]byte{ []byte("//go:generate genny "), } func subIntoLiteral(lit, typeTemplate, specificType string) string { if lit == typeTemplate { return specificType } if !strings.Contains(lit, typeTemplate) { return lit } specificLg := wordify(specificType, true) specificSm := wordify(specificType, false) result := strings.Replace(lit, typeTemplate, specificLg, -1) if strings.HasPrefix(result, specificLg) && !isExported(lit) { return strings.Replace(result, specificLg, specificSm, 1) } return result } func subTypeIntoComment(line, typeTemplate, specificType string) string { var subbed string for _, w := range strings.Fields(line) { subbed = subbed + subIntoLiteral(w, typeTemplate, specificType) + " " } return subbed } // Does the heavy lifting of taking a line of our code and // sbustituting a type into there for our generic type func subTypeIntoLine(line, typeTemplate, specificType string) string { src := []byte(line) var s scanner.Scanner fset := token.NewFileSet() file := fset.AddFile("", fset.Base(), len(src)) s.Init(file, src, nil, scanner.ScanComments) output := "" for { _, tok, lit := s.Scan() if tok == token.EOF { break } else if tok == token.COMMENT { subbed := subTypeIntoComment(lit, typeTemplate, specificType) output = output + subbed + " " } else if tok.IsLiteral() { subbed := subIntoLiteral(lit, typeTemplate, specificType) output = output + subbed + " " } else { output = output + tok.String() + " " } } return output } // typeSet looks like "KeyType: int, ValueType: string" func generateSpecific(filename string, in io.ReadSeeker, typeSet map[string]string) ([]byte, error) { // ensure we are at the beginning of the file in.Seek(0, os.SEEK_SET) // parse the source file fs := token.NewFileSet() file, err := parser.ParseFile(fs, filename, in, 0) if err != nil { return nil, &errSource{Err: err} } // make sure every generic.Type is represented in the types // argument. for _, decl := range file.Decls { switch it := decl.(type) { case *ast.GenDecl: for _, spec := range it.Specs { ts, ok := spec.(*ast.TypeSpec) if !ok { continue } switch tt := ts.Type.(type) { case *ast.SelectorExpr: if name, ok := tt.X.(*ast.Ident); ok { if name.Name == genericPackage { if _, ok := typeSet[ts.Name.Name]; !ok { return nil, &errMissingSpecificType{GenericType: ts.Name.Name} } } } } } } } in.Seek(0, os.SEEK_SET) var buf bytes.Buffer comment := "" scanner := bufio.NewScanner(in) for scanner.Scan() { line := scanner.Text() // does this line contain generic.Type? if strings.Contains(line, genericType) || strings.Contains(line, genericNumber) { comment = "" continue } for t, specificType := range typeSet { if strings.Contains(line, t) { newLine := subTypeIntoLine(line, t, specificType) line = newLine } } if comment != "" { buf.WriteString(makeLine(comment)) comment = "" } // is this line a comment? // TODO: should we handle /* */ comments? if strings.HasPrefix(line, "//") { // record this line to print later comment = line continue } // write the line buf.WriteString(makeLine(line)) } // write it out return buf.Bytes(), nil } // Generics parses the source file and generates the bytes replacing the // generic types for the keys map with the specific types (its value). func Generics(filename, pkgName string, in io.ReadSeeker, typeSets []map[string]string) ([]byte, error) { totalOutput := header for _, typeSet := range typeSets { // generate the specifics parsed, err := generateSpecific(filename, in, typeSet) if err != nil { return nil, err } totalOutput = append(totalOutput, parsed...) } // clean up the code line by line packageFound := false insideImportBlock := false var cleanOutputLines []string scanner := bufio.NewScanner(bytes.NewReader(totalOutput)) for scanner.Scan() { // end of imports block? if insideImportBlock { if bytes.HasSuffix(scanner.Bytes(), closeBrace) { insideImportBlock = false } continue } if bytes.HasPrefix(scanner.Bytes(), packageKeyword) { if packageFound { continue } else { packageFound = true } } else if bytes.HasPrefix(scanner.Bytes(), importKeyword) { if bytes.HasSuffix(scanner.Bytes(), openBrace) { insideImportBlock = true } continue } // check all unwantedLinePrefixes - and skip them skipline := false for _, prefix := range unwantedLinePrefixes { if bytes.HasPrefix(scanner.Bytes(), prefix) { skipline = true continue } } if skipline { continue } cleanOutputLines = append(cleanOutputLines, makeLine(scanner.Text())) } cleanOutput := strings.Join(cleanOutputLines, "") output := []byte(cleanOutput) var err error // change package name if pkgName != "" { output = changePackage(bytes.NewReader([]byte(output)), pkgName) } // fix the imports output, err = imports.Process(filename, output, nil) if err != nil { return nil, &errImports{Err: err} } return output, nil } func makeLine(s string) string { return fmt.Sprintln(strings.TrimRight(s, linefeed)) } // isAlphaNumeric gets whether the rune is alphanumeric or _. func isAlphaNumeric(r rune) bool { return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) } // wordify turns a type into a nice word for function and type // names etc. func wordify(s string, exported bool) string { s = strings.TrimRight(s, "{}") s = strings.TrimLeft(s, "*&") s = strings.Replace(s, ".", "", -1) if !exported { return s } return strings.ToUpper(string(s[0])) + s[1:] } func changePackage(r io.Reader, pkgName string) []byte { var out bytes.Buffer sc := bufio.NewScanner(r) done := false for sc.Scan() { s := sc.Text() if !done && strings.HasPrefix(s, "package") { parts := strings.Split(s, " ") parts[1] = pkgName s = strings.Join(parts, " ") done = true } fmt.Fprintln(&out, s) } return out.Bytes() } func isExported(lit string) bool { if len(lit) == 0 { return false } return unicode.IsUpper(rune(lit[0])) } genny-1.0.0/parse/parse_int_test.go000066400000000000000000000012341334200352200173060ustar00rootroot00000000000000package parse import ( "testing" "github.com/stretchr/testify/assert" ) func TestIsAlphaNumeric(t *testing.T) { for _, r := range []rune{'a', '1', '_', 'A', 'Z'} { assert.True(t, isAlphaNumeric(r)) } for _, r := range []rune{' ', '[', ']', '!', '"'} { assert.False(t, isAlphaNumeric(r)) } } func TestWordify(t *testing.T) { for word, wordified := range map[string]string{ "int": "Int", "*int": "Int", "string": "String", "*MyType": "MyType", "*myType": "MyType", "interface{}": "Interface", "pack.type": "Packtype", "*pack.type": "Packtype", } { assert.Equal(t, wordified, wordify(word, true)) } } genny-1.0.0/parse/parse_test.go000066400000000000000000000105711334200352200164400ustar00rootroot00000000000000package parse_test import ( "io/ioutil" "log" "strings" "testing" "github.com/cheekybits/genny/parse" "github.com/stretchr/testify/assert" ) var tests = []struct { // input filename string pkgName string in string types []map[string]string // expectations expectedOut string expectedErr error }{ { filename: "generic_queue.go", in: `test/queue/generic_queue.go`, types: []map[string]string{{"Something": "int"}}, expectedOut: `test/queue/int_queue.go`, }, { filename: "generic_queue.go", pkgName: "changed", in: `test/queue/generic_queue.go`, types: []map[string]string{{"Something": "int"}}, expectedOut: `test/queue/changed/int_queue.go`, }, { filename: "generic_queue.go", in: `test/queue/generic_queue.go`, types: []map[string]string{{"Something": "float32"}}, expectedOut: `test/queue/float32_queue.go`, }, { filename: "generic_simplemap.go", in: `test/multipletypes/generic_simplemap.go`, types: []map[string]string{{"KeyType": "string", "ValueType": "int"}}, expectedOut: `test/multipletypes/string_int_simplemap.go`, }, { filename: "generic_simplemap.go", in: `test/multipletypes/generic_simplemap.go`, types: []map[string]string{{"KeyType": "interface{}", "ValueType": "int"}}, expectedOut: `test/multipletypes/interface_int_simplemap.go`, }, { filename: "generic_simplemap.go", in: `test/multipletypes/generic_simplemap.go`, types: []map[string]string{{"KeyType": "*MyType1", "ValueType": "*MyOtherType"}}, expectedOut: `test/multipletypes/custom_types_simplemap.go`, }, { filename: "generic_internal.go", in: `test/unexported/generic_internal.go`, types: []map[string]string{{"secret": "*myType"}}, expectedOut: `test/unexported/mytype_internal.go`, }, { filename: "generic_simplemap.go", in: `test/multipletypesets/generic_simplemap.go`, types: []map[string]string{ {"KeyType": "int", "ValueType": "string"}, {"KeyType": "float64", "ValueType": "bool"}, }, expectedOut: `test/multipletypesets/many_simplemaps.go`, }, { filename: "generic_number.go", in: `test/numbers/generic_number.go`, types: []map[string]string{{"NumberType": "int"}}, expectedOut: `test/numbers/int_number.go`, }, { filename: "generic_digraph.go", in: `test/bugreports/generic_digraph.go`, types: []map[string]string{{"Node": "int"}}, expectedOut: `test/bugreports/int_digraph.go`, }, { filename: "generic_new_and_make_slice.go", in: `test/bugreports/generic_new_and_make_slice.go`, types: []map[string]string{{"NumberType": "int"}}, expectedOut: `test/bugreports/int_new_and_make_slice.go`, }, { filename: "cell_x.go", in: `test/bugreports/cell_x.go`, types: []map[string]string{{"X": "int"}}, expectedOut: `test/bugreports/cell_int.go`, }, { filename: "interface_generic_type.go", in: `test/bugreports/interface_generic_type.go`, types: []map[string]string{{"GenericType": "uint8"}}, expectedOut: `test/bugreports/interface_uint8.go`, }, { filename: "negation_generic.go", in: `test/bugreports/negation_generic.go`, types: []map[string]string{{"SomeThing": "string"}}, expectedOut: `test/bugreports/negation_string.go`, }, } func TestParse(t *testing.T) { for _, test := range tests { test.in = contents(test.in) test.expectedOut = contents(test.expectedOut) bytes, err := parse.Generics(test.filename, test.pkgName, strings.NewReader(test.in), test.types) // check the error if test.expectedErr == nil { assert.NoError(t, err, "(%s) No error was expected but got: %s", test.filename, err) } else { assert.NotNil(t, err, "(%s) No error was returned by one was expected: %s", test.filename, test.expectedErr) assert.IsType(t, test.expectedErr, err, "(%s) Generate should return object of type %v", test.filename, test.expectedErr) } // assert the response if !assert.Equal(t, string(bytes), test.expectedOut, "Parse didn't generate the expected output.") { log.Println("EXPECTED: " + test.expectedOut) log.Println("ACTUAL: " + string(bytes)) } } } func contents(s string) string { if strings.HasSuffix(s, "go") { file, err := ioutil.ReadFile(s) if err != nil { panic(err) } return string(file) } return s } genny-1.0.0/parse/test/000077500000000000000000000000001334200352200147135ustar00rootroot00000000000000genny-1.0.0/parse/test/bugreports/000077500000000000000000000000001334200352200171075ustar00rootroot00000000000000genny-1.0.0/parse/test/bugreports/cell_int.go000066400000000000000000000011451334200352200212300ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package bugreports // CellInt is result of generating code via genny for type int type CellInt struct { Value int } const constantInt = 1 func funcInt(p CellInt) {} // exampleInt does some instantation and function calls for types inclueded in this file. // Targets github issue 15 func exampleInt() { aCellInt := CellInt{} anotherCellInt := CellInt{} if aCellInt != anotherCellInt { println(constantInt) panic(constantInt) } funcInt(CellInt{}) } genny-1.0.0/parse/test/bugreports/cell_x.go000066400000000000000000000010261334200352200207030ustar00rootroot00000000000000package bugreports import "github.com/cheekybits/genny/generic" // X is the type generic type used in tests type X generic.Type // CellX is result of generating code via genny for type X type CellX struct { Value X } const constantX = 1 func funcX(p CellX) {} // exampleX does some instantation and function calls for types inclueded in this file. // Targets github issue 15 func exampleX() { aCellX := CellX{} anotherCellX := CellX{} if aCellX != anotherCellX { println(constantX) panic(constantX) } funcX(CellX{}) } genny-1.0.0/parse/test/bugreports/generic_digraph.go000066400000000000000000000007211334200352200225500ustar00rootroot00000000000000package bugreports import "github.com/cheekybits/genny/generic" type Node generic.Type type DigraphNode struct { nodes map[Node][]Node } func NewDigraphNode() *DigraphNode { return &DigraphNode{ nodes: make(map[Node][]Node), } } func (dig *DigraphNode) Add(n Node) { if _, exists := dig.nodes[n]; exists { return } dig.nodes[n] = nil } func (dig *DigraphNode) Connect(a, b Node) { dig.Add(a) dig.Add(b) dig.nodes[a] = append(dig.nodes[a], b) } genny-1.0.0/parse/test/bugreports/generic_new_and_make_slice.go000066400000000000000000000007211334200352200247210ustar00rootroot00000000000000package bugreports import "github.com/cheekybits/genny/generic" // NumberType will be replaced in tests type NumberType generic.Number // ObjNumberType is the struct used for tests. type ObjNumberType struct { v NumberType } // NewNumberTypes calls new on ObjNumberType and instantiates slice. // Targets github issues #36 and #49 func NewNumberTypes() (*ObjNumberType, []ObjNumberType) { n := new(ObjNumberType) m := make([]ObjNumberType, 0) return n, m } genny-1.0.0/parse/test/bugreports/int_digraph.go000066400000000000000000000010271334200352200217260ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package bugreports type DigraphInt struct { nodes map[int][]int } func NewDigraphInt() *DigraphInt { return &DigraphInt{ nodes: make(map[int][]int), } } func (dig *DigraphInt) Add(n int) { if _, exists := dig.nodes[n]; exists { return } dig.nodes[n] = nil } func (dig *DigraphInt) Connect(a, b int) { dig.Add(a) dig.Add(b) dig.nodes[a] = append(dig.nodes[a], b) } genny-1.0.0/parse/test/bugreports/int_new_and_make_slice.go000066400000000000000000000006551334200352200241050ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package bugreports // ObjInt is the struct used for tests. type ObjInt struct { v int } // NewInts calls new on ObjInt and instantiates slice. // Targets github issues #36 and #49 func NewInts() (*ObjInt, []ObjInt) { n := new(ObjInt) m := make([]ObjInt, 0) return n, m } genny-1.0.0/parse/test/bugreports/interface_generic_type.go000066400000000000000000000005121334200352200241310ustar00rootroot00000000000000package bugreports import "github.com/cheekybits/genny/generic" type GenericType generic.Type type InterfaceGenericType interface { DoSomthingGenericType() } // Call calls a method on an instance of generic interface. // Targets github issue 49 func CallWithGenericType(i InterfaceGenericType) { i.DoSomthingGenericType() } genny-1.0.0/parse/test/bugreports/interface_uint8.go000066400000000000000000000005671334200352200225350ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package bugreports type InterfaceUint8 interface { DoSomthingUint8() } // Call calls a method on an instance of generic interface. // Targets github issue 49 func CallWithUint8(i InterfaceUint8) { i.DoSomthingUint8() } genny-1.0.0/parse/test/bugreports/negation_generic.go000066400000000000000000000006201334200352200227340ustar00rootroot00000000000000package bugreports import "github.com/cheekybits/genny/generic" type SomeThing generic.Type func ContainsSomeThing(slice []SomeThing, element SomeThing) bool { return false } // ContainsAllSomeThing targets github issue 36 func ContainsAllSomeThing(slice []SomeThing, other []SomeThing) bool { for _, e := range other { if !ContainsSomeThing(slice, e) { return false } } return true } genny-1.0.0/parse/test/bugreports/negation_string.go000066400000000000000000000007051334200352200226320ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package bugreports func ContainsString(slice []string, element string) bool { return false } // ContainsAllString targets github issue 36 func ContainsAllString(slice []string, other []string) bool { for _, e := range other { if !ContainsString(slice, e) { return false } } return true } genny-1.0.0/parse/test/multipletypes/000077500000000000000000000000001334200352200176335ustar00rootroot00000000000000genny-1.0.0/parse/test/multipletypes/custom_types.go000066400000000000000000000001101334200352200227100ustar00rootroot00000000000000package multipletypes type MyType1 struct{} type MyOtherType struct{} genny-1.0.0/parse/test/multipletypes/custom_types_simplemap.go000066400000000000000000000010071334200352200247650ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package multipletypes type MyType1MyOtherTypeMap map[*MyType1]*MyOtherType func (m MyType1MyOtherTypeMap) Has(key *MyType1) bool { _, ok := m[key] return ok } func (m MyType1MyOtherTypeMap) Get(key *MyType1) *MyOtherType { return m[key] } func (m MyType1MyOtherTypeMap) Set(key *MyType1, value *MyOtherType) MyType1MyOtherTypeMap { m[key] = value return m } genny-1.0.0/parse/test/multipletypes/generic_simplemap.go000066400000000000000000000006751334200352200236550ustar00rootroot00000000000000package multipletypes import "github.com/cheekybits/genny/generic" type KeyType generic.Type type ValueType generic.Type type KeyTypeValueTypeMap map[KeyType]ValueType func (m KeyTypeValueTypeMap) Has(key KeyType) bool { _, ok := m[key] return ok } func (m KeyTypeValueTypeMap) Get(key KeyType) ValueType { return m[key] } func (m KeyTypeValueTypeMap) Set(key KeyType, value ValueType) KeyTypeValueTypeMap { m[key] = value return m } genny-1.0.0/parse/test/multipletypes/generic_simplemap_test.go000066400000000000000000000012171334200352200247050ustar00rootroot00000000000000package multipletypes import ( "testing" "github.com/stretchr/testify/assert" ) func TestSimpleMap(t *testing.T) { key1 := new(KeyType) key2 := new(KeyType) value1 := new(ValueType) m := make(KeyTypeValueTypeMap) assert.Equal(t, m, m.Set(key1, value1)) assert.True(t, m.Has(key1)) assert.False(t, m.Has(key2)) assert.Equal(t, value1, m.Get(key1)) } func TestCustomTypesMap(t *testing.T) { key1 := new(MyType1) key2 := new(MyType1) value1 := new(MyOtherType) m := make(MyType1MyOtherTypeMap) assert.Equal(t, m, m.Set(key1, value1)) assert.True(t, m.Has(key1)) assert.False(t, m.Has(key2)) assert.Equal(t, value1, m.Get(key1)) } genny-1.0.0/parse/test/multipletypes/interface_int_simplemap.go000066400000000000000000000007321334200352200250450ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package multipletypes type InterfaceIntMap map[interface{}]int func (m InterfaceIntMap) Has(key interface{}) bool { _, ok := m[key] return ok } func (m InterfaceIntMap) Get(key interface{}) int { return m[key] } func (m InterfaceIntMap) Set(key interface{}, value int) InterfaceIntMap { m[key] = value return m } genny-1.0.0/parse/test/multipletypes/string_int_simplemap.go000066400000000000000000000006671334200352200244220ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package multipletypes type StringIntMap map[string]int func (m StringIntMap) Has(key string) bool { _, ok := m[key] return ok } func (m StringIntMap) Get(key string) int { return m[key] } func (m StringIntMap) Set(key string, value int) StringIntMap { m[key] = value return m } genny-1.0.0/parse/test/multipletypesets/000077500000000000000000000000001334200352200203475ustar00rootroot00000000000000genny-1.0.0/parse/test/multipletypesets/generic_simplemap.go000066400000000000000000000007411334200352200243630ustar00rootroot00000000000000package multipletypesets import ( "log" "github.com/cheekybits/genny/generic" ) type KeyType generic.Type type ValueType generic.Type type KeyTypeValueTypeMap map[KeyType]ValueType func (m KeyTypeValueTypeMap) Has(key KeyType) bool { _, ok := m[key] return ok } func (m KeyTypeValueTypeMap) Get(key KeyType) ValueType { return m[key] } func (m KeyTypeValueTypeMap) Set(key KeyType, value ValueType) KeyTypeValueTypeMap { log.Println(value) m[key] = value return m } genny-1.0.0/parse/test/multipletypesets/generic_simplemap_test.go000066400000000000000000000005601334200352200254210ustar00rootroot00000000000000package multipletypesets import ( "testing" "github.com/stretchr/testify/assert" ) func TestSimpleMap(t *testing.T) { key1 := new(KeyType) key2 := new(KeyType) value1 := new(ValueType) m := make(KeyTypeValueTypeMap) assert.Equal(t, m, m.Set(key1, value1)) assert.True(t, m.Has(key1)) assert.False(t, m.Has(key2)) assert.Equal(t, value1, m.Get(key1)) } genny-1.0.0/parse/test/multipletypesets/many_simplemaps.go000066400000000000000000000014071334200352200240760ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package multipletypesets import "log" type IntStringMap map[int]string func (m IntStringMap) Has(key int) bool { _, ok := m[key] return ok } func (m IntStringMap) Get(key int) string { return m[key] } func (m IntStringMap) Set(key int, value string) IntStringMap { log.Println(value) m[key] = value return m } type Float64BoolMap map[float64]bool func (m Float64BoolMap) Has(key float64) bool { _, ok := m[key] return ok } func (m Float64BoolMap) Get(key float64) bool { return m[key] } func (m Float64BoolMap) Set(key float64, value bool) Float64BoolMap { log.Println(value) m[key] = value return m } genny-1.0.0/parse/test/numbers/000077500000000000000000000000001334200352200163665ustar00rootroot00000000000000genny-1.0.0/parse/test/numbers/generic_number.go000066400000000000000000000002661334200352200217050ustar00rootroot00000000000000package numbers import "github.com/cheekybits/genny/generic" type NumberType generic.Number func NumberTypeMax(a, b NumberType) NumberType { if a > b { return a } return b } genny-1.0.0/parse/test/numbers/int_number.go000066400000000000000000000003531334200352200210600ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package numbers func IntMax(a, b int) int { if a > b { return a } return b } genny-1.0.0/parse/test/queue/000077500000000000000000000000001334200352200160375ustar00rootroot00000000000000genny-1.0.0/parse/test/queue/changed/000077500000000000000000000000001334200352200174305ustar00rootroot00000000000000genny-1.0.0/parse/test/queue/changed/int_queue.go000066400000000000000000000007321334200352200217570ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package changed // IntQueue is a queue of Ints. type IntQueue struct { items []int } func NewIntQueue() *IntQueue { return &IntQueue{items: make([]int, 0)} } func (q *IntQueue) Push(item int) { q.items = append(q.items, item) } func (q *IntQueue) Pop() int { item := q.items[0] q.items = q.items[1:] return item } genny-1.0.0/parse/test/queue/float32_queue.go000066400000000000000000000010101334200352200210340ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package queue // Float32Queue is a queue of Float32s. type Float32Queue struct { items []float32 } func NewFloat32Queue() *Float32Queue { return &Float32Queue{items: make([]float32, 0)} } func (q *Float32Queue) Push(item float32) { q.items = append(q.items, item) } func (q *Float32Queue) Pop() float32 { item := q.items[0] q.items = q.items[1:] return item } genny-1.0.0/parse/test/queue/generic_queue.go000066400000000000000000000007761334200352200212200ustar00rootroot00000000000000package queue import "github.com/cheekybits/genny/generic" // Something is a type that does something type Something generic.Type // SomethingQueue is a queue of Somethings. type SomethingQueue struct { items []Something } func NewSomethingQueue() *SomethingQueue { return &SomethingQueue{items: make([]Something, 0)} } func (q *SomethingQueue) Push(item Something) { q.items = append(q.items, item) } func (q *SomethingQueue) Pop() Something { item := q.items[0] q.items = q.items[1:] return item } genny-1.0.0/parse/test/queue/generic_queue_test.go000066400000000000000000000034461334200352200222540ustar00rootroot00000000000000package queue import "testing" func TestSomethingQueue(t *testing.T) { var item1 *Something = new(Something) var item2 *Something = new(Something) var item3 *Something = new(Something) q := NewSomethingQueue() q.Push(item1) if len(q.items) != 1 { t.Error("Push should add the item") } q.Push(item2) if len(q.items) != 2 { t.Error("Push should add the item") } q.Push(item3) if len(q.items) != 3 { t.Error("Push should add the item") } if q.Pop() != item1 { t.Error("Pop should return items in the order in which they were added") } if len(q.items) != 2 { t.Error("Pop should remove the item") } if q.Pop() != item2 { t.Error("Pop should return items in the order in which they were added") } if len(q.items) != 1 { t.Error("Pop should remove the item") } if q.Pop() != item3 { t.Error("Pop should return items in the order in which they were added") } if len(q.items) != 0 { t.Error("Pop should remove the item") } } func TestIntQueue(t *testing.T) { var item1 int = 1 var item2 int = 2 var item3 int = 3 q := NewIntQueue() q.Push(item1) if len(q.items) != 1 { t.Error("Push should add the item") } q.Push(item2) if len(q.items) != 2 { t.Error("Push should add the item") } q.Push(item3) if len(q.items) != 3 { t.Error("Push should add the item") } if q.Pop() != item1 { t.Error("Pop should return items in the order in which they were added") } if len(q.items) != 2 { t.Error("Pop should remove the item") } if q.Pop() != item2 { t.Error("Pop should return items in the order in which they were added") } if len(q.items) != 1 { t.Error("Pop should remove the item") } if q.Pop() != item3 { t.Error("Pop should return items in the order in which they were added") } if len(q.items) != 0 { t.Error("Pop should remove the item") } } genny-1.0.0/parse/test/queue/int_queue.go000066400000000000000000000007301334200352200203640ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package queue // IntQueue is a queue of Ints. type IntQueue struct { items []int } func NewIntQueue() *IntQueue { return &IntQueue{items: make([]int, 0)} } func (q *IntQueue) Push(item int) { q.items = append(q.items, item) } func (q *IntQueue) Pop() int { item := q.items[0] q.items = q.items[1:] return item } genny-1.0.0/parse/test/slice/000077500000000000000000000000001334200352200160125ustar00rootroot00000000000000genny-1.0.0/parse/test/slice/ensure_slice.go000066400000000000000000000007271334200352200210270ustar00rootroot00000000000000package slice import ( "log" "reflect" "github.com/cheekybits/genny/generic" ) type MyType generic.Type func EnsureSlice(objectOrSlice interface{}) []MyType { log.Printf("%v", reflect.TypeOf(objectOrSlice)) switch obj := objectOrSlice.(type) { case []MyType: log.Println(" returning it untouched") return obj case MyType, *MyType: log.Println(" wrapping in slice") return []MyType{obj} default: panic("ensure slice needs MyType or []MyType") } } genny-1.0.0/parse/test/slice/ensure_slice_test.go000066400000000000000000000005661334200352200220670ustar00rootroot00000000000000package slice import ( "log" "testing" "github.com/stretchr/testify/assert" ) func TestEnsureSlice(t *testing.T) { myType := new(MyType) slice := EnsureSlice(myType) if assert.NotNil(t, slice) { assert.Equal(t, slice[0], myType) } slice = EnsureSlice(slice) log.Printf("%#v", slice[0]) if assert.NotNil(t, slice) { assert.Equal(t, slice[0], myType) } } genny-1.0.0/parse/test/unexported/000077500000000000000000000000001334200352200171105ustar00rootroot00000000000000genny-1.0.0/parse/test/unexported/custom_types.go000066400000000000000000000000511334200352200221710ustar00rootroot00000000000000package unexported type myType struct{} genny-1.0.0/parse/test/unexported/generic_internal.go000066400000000000000000000002571334200352200227530ustar00rootroot00000000000000package unexported import ( "fmt" "github.com/cheekybits/genny/generic" ) type secret generic.Type func secretInspect(s secret) string { return fmt.Sprintf("%#v", s) } genny-1.0.0/parse/test/unexported/mytype_internal.go000066400000000000000000000004011334200352200226550ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package unexported import "fmt" func myTypeInspect(s *myType) string { return fmt.Sprintf("%#v", s) } genny-1.0.0/parse/typesets.go000066400000000000000000000040761334200352200161520ustar00rootroot00000000000000package parse import "strings" const ( typeSep = " " keyValueSep = "=" valuesSep = "," builtins = "BUILTINS" numbers = "NUMBERS" ) // TypeSet turns a type string into a []map[string]string // that can be given to parse.Generics for it to do its magic. // // Acceptable args are: // // Person=man // Person=man Animal=dog // Person=man Animal=dog Animal2=cat // Person=man,woman Animal=dog,cat // Person=man,woman,child Animal=dog,cat Place=london,paris func TypeSet(arg string) ([]map[string]string, error) { types := make(map[string][]string) var keys []string for _, pair := range strings.Split(arg, typeSep) { segs := strings.Split(pair, keyValueSep) if len(segs) != 2 { return nil, &errBadTypeArgs{Arg: arg, Message: "Generic=Specific expected"} } key := segs[0] keys = append(keys, key) types[key] = make([]string, 0) for _, t := range strings.Split(segs[1], valuesSep) { if t == builtins { types[key] = append(types[key], Builtins...) } else if t == numbers { types[key] = append(types[key], Numbers...) } else { types[key] = append(types[key], t) } } } cursors := make(map[string]int) for _, key := range keys { cursors[key] = 0 } outChan := make(chan map[string]string) go func() { buildTypeSet(keys, 0, cursors, types, outChan) close(outChan) }() var typeSets []map[string]string for typeSet := range outChan { typeSets = append(typeSets, typeSet) } return typeSets, nil } func buildTypeSet(keys []string, keyI int, cursors map[string]int, types map[string][]string, out chan<- map[string]string) { key := keys[keyI] for cursors[key] < len(types[key]) { if keyI < len(keys)-1 { buildTypeSet(keys, keyI+1, copycursors(cursors), types, out) } else { // build the typeset for this combination ts := make(map[string]string) for k, vals := range types { ts[k] = vals[cursors[k]] } out <- ts } cursors[key]++ } } func copycursors(source map[string]int) map[string]int { copy := make(map[string]int) for k, v := range source { copy[k] = v } return copy } genny-1.0.0/parse/typesets_test.go000066400000000000000000000040401334200352200172000ustar00rootroot00000000000000package parse_test import ( "testing" "github.com/cheekybits/genny/parse" "github.com/stretchr/testify/assert" ) func TestArgsToTypeset(t *testing.T) { args := "Person=man,woman Animal=dog,cat Place=london,paris" ts, err := parse.TypeSet(args) if assert.NoError(t, err) { if assert.Equal(t, 8, len(ts)) { assert.Equal(t, ts[0]["Person"], "man") assert.Equal(t, ts[0]["Animal"], "dog") assert.Equal(t, ts[0]["Place"], "london") assert.Equal(t, ts[1]["Person"], "man") assert.Equal(t, ts[1]["Animal"], "dog") assert.Equal(t, ts[1]["Place"], "paris") assert.Equal(t, ts[2]["Person"], "man") assert.Equal(t, ts[2]["Animal"], "cat") assert.Equal(t, ts[2]["Place"], "london") assert.Equal(t, ts[3]["Person"], "man") assert.Equal(t, ts[3]["Animal"], "cat") assert.Equal(t, ts[3]["Place"], "paris") assert.Equal(t, ts[4]["Person"], "woman") assert.Equal(t, ts[4]["Animal"], "dog") assert.Equal(t, ts[4]["Place"], "london") assert.Equal(t, ts[5]["Person"], "woman") assert.Equal(t, ts[5]["Animal"], "dog") assert.Equal(t, ts[5]["Place"], "paris") assert.Equal(t, ts[6]["Person"], "woman") assert.Equal(t, ts[6]["Animal"], "cat") assert.Equal(t, ts[6]["Place"], "london") assert.Equal(t, ts[7]["Person"], "woman") assert.Equal(t, ts[7]["Animal"], "cat") assert.Equal(t, ts[7]["Place"], "paris") } } ts, err = parse.TypeSet("Person=man Animal=dog Place=london") if assert.NoError(t, err) { assert.Equal(t, 1, len(ts)) } ts, err = parse.TypeSet("Person=1,2,3,4,5 Animal=1,2,3,4,5 Place=1,2,3,4,5") if assert.NoError(t, err) { assert.Equal(t, 125, len(ts)) } ts, err = parse.TypeSet("Person=1 Animal=1,2,3,4,5 Place=1,2") if assert.NoError(t, err) { assert.Equal(t, 10, len(ts)) } ts, err = parse.TypeSet("Person=interface{} Animal=interface{} Place=interface{}") if assert.NoError(t, err) { assert.Equal(t, 1, len(ts)) assert.Equal(t, ts[0]["Animal"], "interface{}") assert.Equal(t, ts[0]["Person"], "interface{}") assert.Equal(t, ts[0]["Place"], "interface{}") } }