Files
kubevela/pkg/definition/defkit/array_builder.go
Ayush Kumar 1396884a44 Feat: Implement array builder and enhance CUE generation for X-Definitions (#7039)
* feat: implement array builder and enhance CUE generation with conditional outputs

Signed-off-by: Amit Singh <singhamitch@outlook.com>
Co-authored-by: Amit Singh <singhamitch@outlook.com>
Co-authored-by: Amit Singh <singhamitch@outlook.com>
Signed-off-by: Amit Singh <singhamitch@outlook.com>

* feat: enhance CUE generation for struct fields and add support for array element types

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>
Co-authored-by: Amit Singh <singhamitch@outlook.com>
Signed-off-by: Amit Singh <singhamitch@outlook.com>

* feat: refactor CUE generation logic for struct fields and streamline resource output handling

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

---------

Signed-off-by: Amit Singh <singhamitch@outlook.com>
Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>
Co-authored-by: Amit Singh <singhamitch@outlook.com>
2026-02-12 14:27:56 +00:00

284 lines
8.9 KiB
Go

/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package defkit
// entryKind describes what kind of array entry this is.
type entryKind int
const (
entryStatic entryKind = iota // always-present item
entryConditional // conditional item (if cond { item })
entryForEach // iterated item (for v in source { item })
)
// arrayEntry is a single entry in an ArrayBuilder.
type arrayEntry struct {
kind entryKind
element *ArrayElement // the item fields (for static, conditional, forEach)
cond Condition // for conditional entries
source Value // for forEach entries (iteration source)
guard Condition // for forEach entries (optional guard: if source != _|_)
itemBuilder *ItemBuilder // for forEachWith entries (complex per-item logic)
}
// ArrayBuilder builds CUE arrays with static items, conditional items, and for-each items.
// This is the core type for building arrays where some items are always present,
// some are conditional, and some come from iteration.
//
// Example:
//
// NewArray().
// Item(cpuMetric).
// ItemIf(mem.IsSet(), memMetric).
// ForEachGuarded(podCustomMetrics.IsSet(), podCustomMetrics, customMetric)
type ArrayBuilder struct {
entries []arrayEntry
}
func (a *ArrayBuilder) value() {}
func (a *ArrayBuilder) expr() {}
// NewArray creates a new ArrayBuilder.
func NewArray() *ArrayBuilder {
return &ArrayBuilder{
entries: make([]arrayEntry, 0),
}
}
// Item adds an always-present item to the array.
func (a *ArrayBuilder) Item(elem *ArrayElement) *ArrayBuilder {
a.entries = append(a.entries, arrayEntry{
kind: entryStatic,
element: elem,
})
return a
}
// ItemIf adds a conditional item to the array.
// The item is only included when the condition is true.
func (a *ArrayBuilder) ItemIf(cond Condition, elem *ArrayElement) *ArrayBuilder {
a.entries = append(a.entries, arrayEntry{
kind: entryConditional,
element: elem,
cond: cond,
})
return a
}
// ForEach adds an iterated item to the array.
// For each element in source, an item is created using the element template.
// In the element template, use Reference("m.field") to reference iteration variable fields.
func (a *ArrayBuilder) ForEach(source Value, elem *ArrayElement) *ArrayBuilder {
a.entries = append(a.entries, arrayEntry{
kind: entryForEach,
element: elem,
source: source,
})
return a
}
// ForEachGuarded adds a guarded iterated item to the array.
// The guard condition (typically source.IsSet()) wraps the for loop.
// Generates: if guard for m in source { item }
func (a *ArrayBuilder) ForEachGuarded(guard Condition, source Value, elem *ArrayElement) *ArrayBuilder {
a.entries = append(a.entries, arrayEntry{
kind: entryForEach,
element: elem,
source: source,
guard: guard,
})
return a
}
// Entries returns all entries in the array builder.
func (a *ArrayBuilder) Entries() []arrayEntry { return a.entries }
// entryForEachWith indicates a complex iterated item using an ItemBuilder.
const entryForEachWith entryKind = 3
// ForEachWith adds a complex iterated item to the array, using an ItemBuilder
// callback for per-item operations like conditionals, let bindings, and defaults.
// Uses "v" as the default iteration variable name.
func (a *ArrayBuilder) ForEachWith(source Value, fn func(item *ItemBuilder)) *ArrayBuilder {
return a.ForEachWithVar("v", source, fn)
}
// ForEachWithVar is like ForEachWith but allows specifying the iteration variable name.
func (a *ArrayBuilder) ForEachWithVar(varName string, source Value, fn func(item *ItemBuilder)) *ArrayBuilder {
ib := &ItemBuilder{varName: varName, ops: make([]itemOp, 0)}
fn(ib)
a.entries = append(a.entries, arrayEntry{
kind: entryForEachWith,
source: source,
itemBuilder: ib,
})
return a
}
// --- ItemBuilder ---
// itemOp is a single operation recorded by the ItemBuilder.
type itemOp interface {
isItemOp()
}
// setOp records an unconditional field assignment.
type setOp struct {
field string
value Value
}
func (setOp) isItemOp() {}
// ifBlockOp records a conditional block of nested operations.
type ifBlockOp struct {
cond Condition
body []itemOp
}
func (ifBlockOp) isItemOp() {}
// letOp records a private field binding (_name: value).
type letOp struct {
name string
value Value
}
func (letOp) isItemOp() {}
// setDefaultOp records a CUE default value: field: *defValue | typeName.
type setDefaultOp struct {
field string
defValue Value
typeName string
}
func (setDefaultOp) isItemOp() {}
// ItemBuilder records per-item operations for complex ForEach iterations.
// It supports field assignment, conditionals, let bindings, and CUE default values.
type ItemBuilder struct {
varName string
ops []itemOp
}
// Var returns a reference builder for the iteration variable.
// Use v.Field("port") to reference v.port in CUE.
func (b *ItemBuilder) Var() *IterVarBuilder {
return &IterVarBuilder{varName: b.varName}
}
// Set records an unconditional field assignment.
func (b *ItemBuilder) Set(field string, value Value) {
b.ops = append(b.ops, setOp{field: field, value: value})
}
// If records a conditional block of operations.
func (b *ItemBuilder) If(cond Condition, fn func()) {
outer := b.ops
b.ops = make([]itemOp, 0)
fn()
inner := b.ops
b.ops = outer
b.ops = append(b.ops, ifBlockOp{cond: cond, body: inner})
}
// IfSet records a conditional block that executes when the iteration variable's field is set.
// Generates CUE: if v.field != _|_ { ... }
func (b *ItemBuilder) IfSet(field string, fn func()) {
cond := &IterFieldExistsCondition{varName: b.varName, field: field}
b.If(cond, fn)
}
// IfNotSet records a conditional block that executes when the iteration variable's field is NOT set.
// Generates CUE: if v.field == _|_ { ... }
func (b *ItemBuilder) IfNotSet(field string, fn func()) {
cond := &IterFieldExistsCondition{varName: b.varName, field: field, negate: true}
b.If(cond, fn)
}
// Let records a private field binding and returns a reference to it.
// Generates CUE: _name: value
func (b *ItemBuilder) Let(name string, value Value) Value {
b.ops = append(b.ops, letOp{name: name, value: value})
return &IterLetRef{name: name}
}
// SetDefault records a CUE default value assignment.
// Generates CUE: field: *defValue | typeName
func (b *ItemBuilder) SetDefault(field string, defValue Value, typeName string) {
b.ops = append(b.ops, setDefaultOp{field: field, defValue: defValue, typeName: typeName})
}
// FieldExists returns a Condition that checks if the iteration variable's field is set.
// Generates CUE: v.field != _|_
func (b *ItemBuilder) FieldExists(field string) Condition {
return &IterFieldExistsCondition{varName: b.varName, field: field}
}
// FieldNotExists returns a Condition that checks if the iteration variable's field is NOT set.
// Generates CUE: v.field == _|_
func (b *ItemBuilder) FieldNotExists(field string) Condition {
return &IterFieldExistsCondition{varName: b.varName, field: field, negate: true}
}
// Ops returns the recorded operations.
func (b *ItemBuilder) Ops() []itemOp { return b.ops }
// VarName returns the iteration variable name.
func (b *ItemBuilder) VarName() string { return b.varName }
// IterVarBuilder provides access to iteration variable fields.
type IterVarBuilder struct {
varName string
}
// Ref returns a Value referencing the iteration variable itself.
// Generates CUE: v (or whatever the variable name is).
// Useful when iterating over primitive arrays (e.g., [...int]).
func (v *IterVarBuilder) Ref() *IterVarRef {
return &IterVarRef{varName: v.varName}
}
// Field returns a Value referencing the iteration variable's field.
// v.Field("port") generates CUE: v.port
func (v *IterVarBuilder) Field(name string) *IterFieldRef {
return &IterFieldRef{varName: v.varName, field: name}
}
// ArrayConcatValue represents an array concatenation: left + right.
// Used for expressions like [items] + parameter.extraVolumeMounts.
type ArrayConcatValue struct {
left Value
right Value
}
func (a *ArrayConcatValue) value() {}
func (a *ArrayConcatValue) expr() {}
// Left returns the left operand.
func (a *ArrayConcatValue) Left() Value { return a.left }
// Right returns the right operand.
func (a *ArrayConcatValue) Right() Value { return a.right }
// ArrayConcat creates an array concatenation expression.
// Generates CUE: left + right
func ArrayConcat(left, right Value) *ArrayConcatValue {
return &ArrayConcatValue{left: left, right: right}
}