diff --git a/pkg/definition/defkit/cuegen.go b/pkg/definition/defkit/cuegen.go index 7cbaa417c..164d8da86 100644 --- a/pkg/definition/defkit/cuegen.go +++ b/pkg/definition/defkit/cuegen.go @@ -559,6 +559,13 @@ func (g *CUEGenerator) writeStructFieldForHelper(sb *strings.Builder, f *StructF case f.FieldType() == ParamTypeArray && f.GetElementType() != "": elemCUE := g.cueTypeForParamType(f.GetElementType()) sb.WriteString(fmt.Sprintf("%s%s%s: [...%s]\n", indent, name, optional, elemCUE)) + case len(f.GetEnumValues()) > 0: + // Enum without default: "value1" | "value2" + var enumParts []string + for _, v := range f.GetEnumValues() { + enumParts = append(enumParts, fmt.Sprintf("%q", v)) + } + sb.WriteString(fmt.Sprintf("%s%s%s: %s\n", indent, name, optional, strings.Join(enumParts, " | "))) default: sb.WriteString(fmt.Sprintf("%s%s%s: %s\n", indent, name, optional, fieldType)) } @@ -2991,6 +2998,13 @@ func (g *CUEGenerator) writeStructField(sb *strings.Builder, f *StructField, dep case f.FieldType() == ParamTypeArray && f.GetElementType() != "": elemCUE := g.cueTypeForParamType(f.GetElementType()) sb.WriteString(fmt.Sprintf("%s%s%s: [...%s]\n", indent, name, optional, elemCUE)) + case len(f.GetEnumValues()) > 0: + // Enum without default: "value1" | "value2" + var enumParts []string + for _, v := range f.GetEnumValues() { + enumParts = append(enumParts, fmt.Sprintf("%q", v)) + } + sb.WriteString(fmt.Sprintf("%s%s%s: %s\n", indent, name, optional, strings.Join(enumParts, " | "))) default: sb.WriteString(fmt.Sprintf("%s%s%s: %s\n", indent, name, optional, fieldType)) } diff --git a/pkg/definition/defkit/cuegen_test.go b/pkg/definition/defkit/cuegen_test.go index 9e9c4983f..6fcd4b555 100644 --- a/pkg/definition/defkit/cuegen_test.go +++ b/pkg/definition/defkit/cuegen_test.go @@ -1046,4 +1046,110 @@ var _ = Describe("CUEGenerator", func() { Expect(cue).To(ContainSubstring(`protocol: "TCP"`)) }) }) + + Describe("Struct field enum generation in helper definitions", func() { + It("should generate enum with default on a helper struct field", func() { + rule := defkit.Struct("rule").Fields( + defkit.Field("strategy", defkit.ParamTypeString). + Default("onAppUpdate"). + Enum("onAppUpdate", "onAppDelete", "never"), + ) + + p := defkit.NewPolicy("test-enum-default"). + Description("Test"). + Helper("Rule", rule) + + cue := p.ToCue() + + Expect(cue).To(ContainSubstring(`strategy: *"onAppUpdate" | "onAppDelete" | "never"`)) + Expect(cue).NotTo(ContainSubstring(`strategy: *"onAppUpdate" | string`)) + }) + + It("should generate enum without default on a helper struct field", func() { + rule := defkit.Struct("rule").Fields( + defkit.Field("propagation", defkit.ParamTypeString). + Enum("orphan", "cascading"). + Optional(), + ) + + p := defkit.NewPolicy("test-enum-no-default"). + Description("Test"). + Helper("Rule", rule) + + cue := p.ToCue() + + Expect(cue).To(ContainSubstring(`propagation?: "orphan" | "cascading"`)) + Expect(cue).NotTo(ContainSubstring("propagation?: string")) + }) + + It("should generate required enum without default on a helper struct field", func() { + rule := defkit.Struct("rule").Fields( + defkit.Field("mode", defkit.ParamTypeString). + Enum("strict", "permissive"). + Required(), + ) + + p := defkit.NewPolicy("test-enum-required"). + Description("Test"). + Helper("Rule", rule) + + cue := p.ToCue() + + Expect(cue).To(ContainSubstring(`mode: "strict" | "permissive"`)) + Expect(cue).NotTo(ContainSubstring("mode?: ")) + Expect(cue).NotTo(ContainSubstring("mode: string")) + }) + + It("should generate enum without default on a parameter struct field", func() { + comp := defkit.NewComponent("test-param-enum"). + Workload("v1", "Pod"). + Params( + defkit.Struct("config").Fields( + defkit.Field("level", defkit.ParamTypeString). + Enum("low", "medium", "high"). + Optional(), + defkit.Field("mode", defkit.ParamTypeString). + Enum("fast", "safe"). + Required(), + ), + ). + Template(func(tpl *defkit.Template) { + tpl.Output(defkit.NewResource("v1", "Pod")) + }) + + cue := defkit.NewCUEGenerator().GenerateFullDefinition(comp) + + Expect(cue).To(ContainSubstring(`level?: "low" | "medium" | "high"`)) + Expect(cue).To(ContainSubstring(`mode: "fast" | "safe"`)) + Expect(cue).NotTo(ContainSubstring("level?: string")) + Expect(cue).NotTo(ContainSubstring("mode: string")) + Expect(cue).NotTo(ContainSubstring("mode?: ")) + }) + + It("should handle mixed enum fields: with default, without default, and plain string", func() { + rule := defkit.Struct("rule").Fields( + defkit.Field("strategy", defkit.ParamTypeString). + Default("always"). + Enum("always", "never", "on-failure"), + defkit.Field("propagation", defkit.ParamTypeString). + Enum("orphan", "cascading"). + Optional(), + defkit.Field("name", defkit.ParamTypeString). + Optional(), + ) + + p := defkit.NewPolicy("test-mixed"). + Description("Test"). + Helper("Rule", rule) + + cue := p.ToCue() + + // Enum with default + Expect(cue).To(ContainSubstring(`strategy: *"always" | "never" | "on-failure"`)) + // Enum without default (optional) + Expect(cue).To(ContainSubstring(`propagation?: "orphan" | "cascading"`)) + // Plain string (optional) + Expect(cue).To(ContainSubstring("name?: string")) + }) + }) })