Skip to content

Commit 294b712

Browse files
authored
Adds support for primitive alias types and enums (#28)
1 parent 4a674ee commit 294b712

14 files changed

Lines changed: 318 additions & 4 deletions

File tree

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,29 @@ package mytypes
8080
// +kanopy:builder=package
8181
```
8282

83+
## Generate Enums
84+
85+
An enum can be generated with the following argument. Enum constants are used in several upstream k8s packages.
86+
e.g.
87+
88+
```golang
89+
// +kanopy:builder=true,enum=*;CREATE;UPDATE;DELETE;CONNECT
90+
type OperationType admissionv1.OperationType
91+
```
92+
93+
Which generates:
94+
```golang
95+
const OperationTypeCreate OperationType = "CREATE"
96+
```
97+
98+
The `*` is a special value within k8s that indicates `All`. The code generator will translate a `*` to all in the suffix of the constant name.
99+
100+
```golang
101+
const OperationTypeAll OperationType = "*"
102+
```
103+
104+
Enum arguments can be used for both upstream packages and internal packages.
105+
83106
## Definition of Terms
84107

85108
| terms | definition |

pkg/generators/builder/builder.go

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ func (b *BuilderPatternGenerator) GenerateType(c *generator.Context, t *types.Ty
108108

109109
sw := generator.NewSnippetWriter(w, c, "$", "$")
110110

111+
if t.IsPrimitive() {
112+
sw.Do(snippets.GenerateEnumSetter(t, tags.GetEnumOptions(t)))
113+
return sw.Error()
114+
}
115+
111116
if hasObjectMetaEmbedded(t) {
112117
parentTypeOfObjectMeta := getParentOfEmbeddedType(t, ObjectMeta)
113118
objectMetaType := getMemberTypeFromType(parentTypeOfObjectMeta, ObjectMeta)
@@ -172,6 +177,10 @@ func (b *BuilderPatternGenerator) generateSettersForType(sw *generator.SnippetWr
172177
if b.isTypeEnabled(pointerType) {
173178
sw.Do(setter.GenerateSetterForEmbeddedPointer(m, b.getWrapperType(pointerType)))
174179
}
180+
case types.Alias:
181+
if b.isTypePrimitiveEnabled(m) {
182+
sw.Do(setter.GenerateSetterForAliasPointerPrimitive(m, m.Type))
183+
}
175184
default:
176185
sw.Do(setter.GenerateSetterForType(m))
177186
}
@@ -207,6 +216,12 @@ func (b *BuilderPatternGenerator) isTypeEnabled(t *types.Type) bool {
207216
return exists
208217
}
209218

219+
func (b *BuilderPatternGenerator) isTypePrimitiveEnabled(t types.Member) bool {
220+
typeName := t.Name
221+
_, exists := b.enabledTypes[typeName]
222+
return exists
223+
}
224+
210225
func (b *BuilderPatternGenerator) getWrapperType(t *types.Type) *types.Type {
211226
typeName := t.Name.String()
212227
if parent, ok := b.enabledTypes[typeName]; ok {
@@ -257,16 +272,28 @@ func (b *BuilderPatternGenerator) Filter(c *generator.Context, t *types.Type) bo
257272
return false
258273
}
259274

275+
b.enablePrimitiveType(t)
276+
260277
for _, m := range t.Members {
261-
if m.Embedded {
262-
childType := m.Type.Name.String()
263-
b.enabledTypes[childType] = t
264-
}
278+
b.enableEmbededMemberType(m, t)
265279
}
266280

267281
return true
268282
}
269283

284+
func (b *BuilderPatternGenerator) enableEmbededMemberType(m types.Member, t *types.Type) {
285+
if m.Embedded {
286+
childType := m.Type.Name.String()
287+
b.enabledTypes[childType] = t
288+
}
289+
}
290+
291+
func (b *BuilderPatternGenerator) enablePrimitiveType(t *types.Type) {
292+
if t.IsPrimitive() {
293+
b.enabledTypes[t.Name.Name] = t
294+
}
295+
}
296+
270297
func (b *BuilderPatternGenerator) Namers(c *generator.Context) namer.NameSystems {
271298
return namer.NameSystems{
272299
"raw": namer.NewRawNamer(b.pkgToBuild.Path, b.imports),

pkg/generators/builder/builder_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,30 @@ func TestBuilderPattern_NonObjectMetaGeneratesSnippets(t *testing.T) {
126126
assert.NotContains(t, buf.String(), "DeepCopyInto")
127127
}
128128

129+
func TestBuilderAliasPrimitiveType(t *testing.T) {
130+
b := &BuilderPatternGeneratorFactory{}
131+
pkg, typeToGenerate := newTestGeneratorType(t, "d", "DPolicyRule")
132+
_, aliasToGenerate := newTestGeneratorType(t, "d", "AliasType")
133+
g := b.NewBuilder(pkg)
134+
buf := &bytes.Buffer{}
135+
c := newGeneratorContext(g)
136+
assert.True(t, g.Filter(c, typeToGenerate))
137+
assert.True(t, g.Filter(c, aliasToGenerate))
138+
assert.NoError(t, g.GenerateType(c, typeToGenerate, buf))
139+
assert.Contains(t, buf.String(), "func (o *DPolicyRule) WithAliasType(in AliasType) *DPolicyRule")
140+
}
141+
142+
func TestBuilderAliasPrimitiveTypeNotGenerated(t *testing.T) {
143+
b := &BuilderPatternGeneratorFactory{}
144+
pkg, typeToGenerate := newTestGeneratorType(t, "d", "DPolicyRule")
145+
g := b.NewBuilder(pkg)
146+
buf := &bytes.Buffer{}
147+
c := newGeneratorContext(g)
148+
assert.True(t, g.Filter(c, typeToGenerate))
149+
assert.NoError(t, g.GenerateType(c, typeToGenerate, buf))
150+
assert.NotContains(t, buf.String(), "func (o *DPolicyRule) WithAliasType(in AliasType) *DPolicyRule")
151+
}
152+
129153
func TestBuilderPattern_GenerateSettersForType(t *testing.T) {
130154
b := &BuilderPatternGeneratorFactory{}
131155
pkg, typeToGenerate := newTestGeneratorType(t, "c", "CDeployment")

pkg/generators/builder/testdata/d/d.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,6 @@ import (
88
type DPolicyRule struct {
99
e.MockPolicyRule
1010
}
11+
12+
// +kanopy:builder=true
13+
type AliasType e.AliasToString

pkg/generators/builder/testdata/d/e/e.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,7 @@ package e
33
type MockPolicyRule struct {
44
Verbs []string
55
ListOfInts []int
6+
AliasType *AliasToString
67
}
8+
9+
type AliasToString string

pkg/generators/snippets/enum.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package snippets
2+
3+
import (
4+
"fmt"
5+
6+
"golang.org/x/text/cases"
7+
"golang.org/x/text/language"
8+
"k8s.io/gengo/generator"
9+
"k8s.io/gengo/types"
10+
)
11+
12+
const allValue = "*"
13+
const allSuffix = "All"
14+
15+
func GenerateEnumSetter(inputType *types.Type, enumOptions []string) (string, generator.Args) {
16+
args := generator.Args{
17+
"type": inputType,
18+
"name": inputType.Name.Name,
19+
}
20+
21+
raw := ""
22+
23+
for _, val := range enumOptions {
24+
raw += fmt.Sprintf(`const $.name$%s $.name$ = "%s"`, toSuffix(val), val)
25+
raw += "\n"
26+
}
27+
28+
return raw, args
29+
}
30+
31+
func toSuffix(v string) string {
32+
suffix := v
33+
if suffix == allValue {
34+
suffix = allSuffix
35+
}
36+
caser := cases.Title(language.English)
37+
return caser.String(suffix)
38+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package snippets
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
"k8s.io/gengo/generator"
9+
"k8s.io/gengo/types"
10+
)
11+
12+
func TestGenerateEnumSetter(t *testing.T) {
13+
t.Parallel()
14+
15+
ctx, err := newTestGeneratorContext()
16+
assert.NoError(t, err)
17+
18+
tests := []struct {
19+
description string
20+
enumVals []string
21+
want string
22+
}{
23+
{
24+
description: "single value",
25+
enumVals: []string{"val1"},
26+
want: "const MyEnumVal1 MyEnum = \"val1\"\n",
27+
},
28+
{
29+
description: "multi value",
30+
enumVals: []string{"val1", "val2"},
31+
want: "const MyEnumVal1 MyEnum = \"val1\"\nconst MyEnumVal2 MyEnum = \"val2\"\n",
32+
},
33+
{
34+
description: "all caps",
35+
enumVals: []string{"VAL"},
36+
want: "const MyEnumVal MyEnum = \"VAL\"\n",
37+
},
38+
}
39+
40+
tt := enumTestType()
41+
42+
for _, test := range tests {
43+
var b bytes.Buffer
44+
sw := generator.NewSnippetWriter(&b, ctx, "$", "$")
45+
sw.Do(GenerateEnumSetter(&tt, test.enumVals))
46+
assert.NoError(t, sw.Error(), test.description)
47+
assert.Equal(t, test.want, b.String(), test.description)
48+
}
49+
}
50+
51+
func enumTestType() types.Type {
52+
tt := types.Type{
53+
Name: types.Name{
54+
Package: "./",
55+
Name: "MyEnum",
56+
Path: "",
57+
},
58+
}
59+
return tt
60+
}

pkg/generators/snippets/setters.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,23 @@ func (o $.pointer$$.type|raw$) $.funcName$(in *$.inputType|raw$) $.pointer$$.typ
231231
return raw, args
232232
}
233233

234+
func (s *Setter) GenerateSetterForAliasPointerPrimitive(member types.Member, inputType *types.Type) (string, generator.Args) {
235+
args := defaultGeneratorArgs(s.Root, s.pointerReceiver)
236+
args["funcName"] = funcName(member)
237+
args["memberAccessor"] = member.Name
238+
args["inputType"] = inputType.Elem
239+
240+
raw := `// $.funcName$ is an autogenerated function
241+
func (o $.pointer$$.type|raw$) $.funcName$(in $.memberAccessor$) $.pointer$$.type|raw$ {
242+
p := $.inputType|raw$(in)
243+
o.$.memberAccessor$ = &p
244+
return o
245+
}
246+
247+
`
248+
return raw, args
249+
}
250+
234251
func funcName(m types.Member) string {
235252
verb := "With"
236253

pkg/generators/snippets/setters_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,31 @@ func TestFuncName(t *testing.T) {
441441
}
442442
}
443443

444+
func TestGenerateSetterForAliasPointerPrimitive(t *testing.T) {
445+
t.Parallel()
446+
447+
ctx, err := newTestGeneratorContext()
448+
require.NoError(t, err)
449+
450+
someStruct := newTestType(t, "SomeStruct")
451+
root := someStruct
452+
member := getMemberFromType(t, someStruct, "SomeStruct", "Alias")
453+
want := `// WithAlias is an autogenerated function
454+
func (o *SomeStruct) WithAlias(in Alias) *SomeStruct {
455+
p := b.AliasOfString(in)
456+
o.Alias = &p
457+
return o
458+
}
459+
460+
`
461+
var b bytes.Buffer
462+
setter := NewSetter(root, member.Type, true)
463+
sw := generator.NewSnippetWriter(&b, ctx, "$", "$")
464+
sw.Do(setter.GenerateSetterForAliasPointerPrimitive(member, member.Type))
465+
assert.NoError(t, sw.Error())
466+
assert.Equal(t, want, b.String())
467+
}
468+
444469
func TestMemberAccessor(t *testing.T) {
445470
t.Parallel()
446471

pkg/generators/snippets/testdata/a/a.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ type SomeStruct struct {
1818
MapStringByteSlice map[string][]byte
1919
Bool bool
2020
PointerBool *bool
21+
Alias *b.AliasOfString
2122
}
2223

2324
type AStruct struct {

0 commit comments

Comments
 (0)