Skip to content

Commit

Permalink
Initial pass at #2321
Browse files Browse the repository at this point in the history
  • Loading branch information
dcarbone committed Aug 12, 2022
1 parent 2304c10 commit 9f6d51e
Show file tree
Hide file tree
Showing 4 changed files with 377 additions and 16 deletions.
112 changes: 112 additions & 0 deletions codegen/templates/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import (
"os"
"path/filepath"
"reflect"
"regexp"
"runtime"
"sort"
"strconv"
"strings"
"sync"
"text/template"
"unicode"

Expand Down Expand Up @@ -202,6 +204,7 @@ func Funcs() template.FuncMap {
"lookupImport": CurrentImports.Lookup,
"go": ToGo,
"goPrivate": ToGoPrivate,
"goModelName": ToGoModelName,
"add": func(a, b int) int {
return a + b
},
Expand Down Expand Up @@ -291,6 +294,115 @@ func Call(p *types.Func) string {
return pkg + p.Name()
}

var (
modelNamesMu sync.Mutex
modelNames = make(map[string]string, 0)
)

func resetModelNames() {
modelNamesMu.Lock()
defer modelNamesMu.Unlock()
modelNames = make(map[string]string, 0)
}

func buildGoModelNameKey(parts []string) string {
const sep = ":"
return strings.Join(parts, sep)
}

func ToGoModelName(parts ...string) string {
modelNamesMu.Lock()
defer modelNamesMu.Unlock()

var (
goNameKey string
partLen int

nameExists = func(n string) bool {
for _, v := range modelNames {
if n == v {
return true
}
}
return false
}

applyToGo = func(parts []string) string {
var out string
for _, p := range parts {
out = fmt.Sprintf("%s%s", out, ToGo(p))
}
return out
}

applyValidGoName = func(parts []string) string {
var out string
for _, p := range parts {
out = fmt.Sprintf("%s%s", out, ValidGoName(p))
}
return out
}
)

// build key for this entity
goNameKey = buildGoModelNameKey(parts)

// determine if we've seen this entity before, and reuse if so
if goName, ok := modelNames[goNameKey]; ok {
return goName
}

// attempt first pass

// test first pass
if goName := applyToGo(parts); !nameExists(goName) {
modelNames[goNameKey] = goName
return goName
}

// determine number of parts
partLen = len(parts)

// if there is only 1 part, append incrementing number until no conflict
if partLen == 1 {
base := applyToGo(parts)
for i := 0; ; i++ {
tmp := fmt.Sprintf("%s%d", base, i)
if !nameExists(tmp) {
modelNames[goNameKey] = tmp
return tmp
}
}
}

// best effort "pretty" name
for i := partLen - 1; i >= 1; i-- {
tmp := fmt.Sprintf("%s%s", applyToGo(parts[0:i]), applyValidGoName(parts[i:]))
if !nameExists(tmp) {
modelNames[goNameKey] = tmp
return tmp
}
}

// finally, fallback to just adding an incrementing number
base := applyToGo(parts)
for i := 0; ; i++ {
tmp := fmt.Sprintf("%s%d", base, i)
if !nameExists(tmp) {
modelNames[goNameKey] = tmp
return tmp
}
}
}

var (
goNameRe = regexp.MustCompile("[^a-zA-Z0-9_]")
)

func ValidGoName(in string) string {
return goNameRe.ReplaceAllString(in, "_")
}

func ToGo(name string) string {
if name == "_" {
return "_"
Expand Down
63 changes: 62 additions & 1 deletion codegen/templates/templates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package templates

import (
"embed"
"fmt"
"os"
"path/filepath"
"testing"
Expand Down Expand Up @@ -74,9 +75,69 @@ func TestToGoPrivate(t *testing.T) {
require.Equal(t, "_", ToGoPrivate("_"))
}

func TestToGoModelName(t *testing.T) {
type aTest struct {
input [][]string
expected []string
}

theTests := []aTest{
{
input: [][]string{{"MyValue"}},
expected: []string{"MyValue"},
},
{
input: [][]string{{"MyValue"}, {"myValue"}},
expected: []string{"MyValue", "MyValue0"},
},
{
input: [][]string{{"MyValue"}, {"YourValue"}},
expected: []string{"MyValue", "YourValue"},
},
{
input: [][]string{{"MyEnumName", "Value"}},
expected: []string{"MyEnumNameValue"},
},
{
input: [][]string{{"MyEnumName", "Value"}, {"MyEnumName", "value"}},
expected: []string{"MyEnumNameValue", "MyEnumNamevalue"},
},
{
input: [][]string{{"MyEnumName", "value"}, {"MyEnumName", "Value"}},
expected: []string{"MyEnumNameValue", "MyEnumNameValue0"},
},
{
input: [][]string{{"MyEnumName", "Value"}, {"MyEnumName", "value"}, {"MyEnumName", "vALue"}, {"MyEnumName", "VALue"}},
expected: []string{"MyEnumNameValue", "MyEnumNamevalue", "MyEnumNameVALue", "MyEnumNameVALue0"},
},
{
input: [][]string{{"MyEnumName", "TitleValue"}, {"MyEnumName", "title_value"}, {"MyEnumName", "title_Value"}, {"MyEnumName", "Title_Value"}},
expected: []string{"MyEnumNameTitleValue", "MyEnumNametitle_value", "MyEnumNametitle_Value", "MyEnumNameTitle_Value"},
},
{
input: [][]string{{"MyEnumName", "TitleValue", "OtherValue"}},
expected: []string{"MyEnumNameTitleValueOtherValue"},
},
{
input: [][]string{{"MyEnumName", "TitleValue", "OtherValue"}, {"MyEnumName", "title_value", "OtherValue"}},
expected: []string{"MyEnumNameTitleValueOtherValue", "MyEnumNametitle_valueOtherValue"},
},
}

for ti, at := range theTests {
resetModelNames()
t.Run(fmt.Sprintf("modelname-%d", ti), func(t *testing.T) {
at := at
for i, n := range at.input {
require.Equal(t, at.expected[i], ToGoModelName(n...))
}
})
}
}

func Test_wordWalker(t *testing.T) {
helper := func(str string) []*wordInfo {
resultList := []*wordInfo{}
var resultList []*wordInfo
wordWalker(str, func(info *wordInfo) {
resultList = append(resultList, info)
})
Expand Down
Loading

0 comments on commit 9f6d51e

Please sign in to comment.