Skip to content

Commit

Permalink
Add the ability to create unexported types from an exported template
Browse files Browse the repository at this point in the history
This allows one to create an exported template type and create a
non exported type from that template.

Fixes #66
  • Loading branch information
jsouthworth committed Oct 19, 2020
1 parent 3e22f1a commit 0d00bd7
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 20 deletions.
11 changes: 6 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func main() {
out = flag.String("out", "", "file to save output to instead of stdout")
pkgName = flag.String("pkg", "", "package name for generated files")
genTag = flag.String("tag", "", "build tag that is stripped from output")
private = flag.Bool("private", false, "don't export the generated type")
prefix = "https://github.com/metabition/gennylib/raw/master/"
)
flag.Parse()
Expand Down Expand Up @@ -84,23 +85,23 @@ func main() {
}
r.Body.Close()
br := bytes.NewReader(b)
err = gen(*in, outputFilename, *pkgName, *genTag, br, typeSets, outWriter)
err = gen(*in, outputFilename, *pkgName, *genTag, br, typeSets, outWriter, *private)
} 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, outputFilename, *pkgName, *genTag, file, typeSets, outWriter)
err = gen(*in, outputFilename, *pkgName, *genTag, file, typeSets, outWriter, *private)
} else {
var source []byte
source, err = ioutil.ReadAll(os.Stdin)
if err != nil {
fatal(exitcodeStdinFailed, err)
}
reader := bytes.NewReader(source)
err = gen("stdin", outputFilename, *pkgName, *genTag, reader, typeSets, outWriter)
err = gen("stdin", outputFilename, *pkgName, *genTag, reader, typeSets, outWriter, *private)
}

// do the work
Expand Down Expand Up @@ -144,12 +145,12 @@ func fatal(code int, a ...interface{}) {
}

// gen performs the generic generation.
func gen(filename, outputFilename, pkgName, tag string, in io.ReadSeeker, typesets []map[string]string, out io.Writer) error {
func gen(filename, outputFilename, pkgName, tag string, in io.ReadSeeker, typesets []map[string]string, out io.Writer, private bool) error {

var output []byte
var err error

output, err = parse.Generics(filename, outputFilename, pkgName, tag, in, typesets)
output, err = parse.Generics(filename, outputFilename, pkgName, tag, in, typesets, private)
if err != nil {
return err
}
Expand Down
30 changes: 17 additions & 13 deletions parse/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,33 +38,34 @@ var unwantedLinePrefixes = [][]byte{
[]byte("//go:generate genny "),
}

func subIntoLiteral(lit, typeTemplate, specificType string) string {
func subIntoLiteral(lit, typeTemplate, specificType string, private bool) string {
if lit == typeTemplate {
return specificType
}
if !strings.Contains(lit, typeTemplate) {
return lit
}
specificLg := wordify(specificType, true)
specificSm := wordify(specificType, false)
specificLg := wordify(specificType, true, private)
specificSm := wordify(specificType, false, private)
result := strings.Replace(lit, typeTemplate, specificLg, -1)
fmt.Println(result, specificLg, isExported(lit), lit, specificSm)
if strings.HasPrefix(result, specificLg) && !isExported(lit) {
return strings.Replace(result, specificLg, specificSm, 1)
}
return result
}

func subTypeIntoComment(line, typeTemplate, specificType string) string {
func subTypeIntoComment(line, typeTemplate, specificType string, private bool) string {
var subbed string
for _, w := range strings.Fields(line) {
subbed = subbed + subIntoLiteral(w, typeTemplate, specificType) + " "
subbed = subbed + subIntoLiteral(w, typeTemplate, specificType, private) + " "
}
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 {
func subTypeIntoLine(line, typeTemplate, specificType string, private bool) string {
src := []byte(line)
var s scanner.Scanner
fset := token.NewFileSet()
Expand All @@ -76,10 +77,10 @@ func subTypeIntoLine(line, typeTemplate, specificType string) string {
if tok == token.EOF {
break
} else if tok == token.COMMENT {
subbed := subTypeIntoComment(lit, typeTemplate, specificType)
subbed := subTypeIntoComment(lit, typeTemplate, specificType, private)
output = output + subbed + " "
} else if tok.IsLiteral() {
subbed := subIntoLiteral(lit, typeTemplate, specificType)
subbed := subIntoLiteral(lit, typeTemplate, specificType, private)
output = output + subbed + " "
} else {
output = output + tok.String() + " "
Expand All @@ -89,7 +90,7 @@ func subTypeIntoLine(line, typeTemplate, specificType string) string {
}

// typeSet looks like "KeyType: int, ValueType: string"
func generateSpecific(filename string, in io.ReadSeeker, typeSet map[string]string) ([]byte, error) {
func generateSpecific(filename string, in io.ReadSeeker, typeSet map[string]string, private bool) ([]byte, error) {

// ensure we are at the beginning of the file
in.Seek(0, os.SEEK_SET)
Expand Down Expand Up @@ -143,7 +144,7 @@ func generateSpecific(filename string, in io.ReadSeeker, typeSet map[string]stri

for t, specificType := range typeSet {
if strings.Contains(line, t) {
newLine := subTypeIntoLine(line, t, specificType)
newLine := subTypeIntoLine(line, t, specificType, private)
line = newLine
}
}
Expand Down Expand Up @@ -171,7 +172,7 @@ func generateSpecific(filename string, in io.ReadSeeker, typeSet map[string]stri

// 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, outputFilename, pkgName, tag string, in io.ReadSeeker, typeSets []map[string]string) ([]byte, error) {
func Generics(filename, outputFilename, pkgName, tag string, in io.ReadSeeker, typeSets []map[string]string, private bool) ([]byte, error) {
var localUnwantedLinePrefixes [][]byte
localUnwantedLinePrefixes = append(localUnwantedLinePrefixes, unwantedLinePrefixes...)

Expand All @@ -184,7 +185,7 @@ func Generics(filename, outputFilename, pkgName, tag string, in io.ReadSeeker, t
for _, typeSet := range typeSets {

// generate the specifics
parsed, err := generateSpecific(filename, in, typeSet)
parsed, err := generateSpecific(filename, in, typeSet, private)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -266,10 +267,13 @@ func isAlphaNumeric(r rune) bool {

// wordify turns a type into a nice word for function and type
// names etc.
func wordify(s string, exported bool) string {
func wordify(s string, exported, private bool) string {
s = strings.TrimRight(s, "{}")
s = strings.TrimLeft(s, "*&")
s = strings.Replace(s, ".", "", -1)
if private {
return strings.ToLower(string(s[0])) + s[1:]
}
if !exported {
return s
}
Expand Down
16 changes: 15 additions & 1 deletion parse/parse_int_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,21 @@ func TestWordify(t *testing.T) {
"pack.type": "Packtype",
"*pack.type": "Packtype",
} {
assert.Equal(t, wordified, wordify(word, true))
assert.Equal(t, wordified, wordify(word, true, false))
}

// Test non exported name generation
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, true))
}

}
10 changes: 9 additions & 1 deletion parse/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var tests = []struct {
in string
tag string
types []map[string]string
private bool

// expectations
expectedOut string
Expand All @@ -36,6 +37,13 @@ var tests = []struct {
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": "int"}},
expectedOut: `test/queue/int_queue_private.go`,
private: true,
},
{
filename: "generic_queue.go",
in: `test/queue/generic_queue.go`,
Expand Down Expand Up @@ -134,7 +142,7 @@ func TestParse(t *testing.T) {
test.in = contents(test.in)
test.expectedOut = contents(test.expectedOut)

bytes, err := parse.Generics(test.filename, test.outputFilename, test.pkgName, test.tag, strings.NewReader(test.in), test.types)
bytes, err := parse.Generics(test.filename, test.outputFilename, test.pkgName, test.tag, strings.NewReader(test.in), test.types, test.private)

// check the error
if test.expectedErr == nil {
Expand Down
22 changes: 22 additions & 0 deletions parse/test/queue/int_queue_private.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// 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
}

0 comments on commit 0d00bd7

Please sign in to comment.