diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validation.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validation.go index 306deaafb3b..d04a123237c 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validation.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validation.go @@ -1093,14 +1093,14 @@ func (g *genValidations) emitCallToOtherTypeFunc(c *generator.Context, node *typ // Emitted code assumes that the value in question is always a pair of nilable // variables named "obj" and "oldObj", and the field path to this value is // named "fldPath". -func emitCallsToValidators(c *generator.Context, validations []validators.FunctionGen, sw *generator.SnippetWriter) { +func emitCallsToValidators(c *generator.Context, validations []*validators.FunctionGen, sw *generator.SnippetWriter) { // Helper func - sort := func(in []validators.FunctionGen) []validators.FunctionGen { - sooner := make([]validators.FunctionGen, 0, len(in)) - later := make([]validators.FunctionGen, 0, len(in)) + sort := func(in []*validators.FunctionGen) []*validators.FunctionGen { + sooner := make([]*validators.FunctionGen, 0, len(in)) + later := make([]*validators.FunctionGen, 0, len(in)) for _, fg := range in { - isShortCircuit := (fg.Flags().IsSet(validators.ShortCircuit)) + isShortCircuit := (fg.Flags.IsSet(validators.ShortCircuit)) if isShortCircuit { sooner = append(sooner, fg) @@ -1116,19 +1116,17 @@ func emitCallsToValidators(c *generator.Context, validations []validators.Functi validations = sort(validations) for _, v := range validations { - isShortCircuit := v.Flags().IsSet(validators.ShortCircuit) - isNonError := v.Flags().IsSet(validators.NonError) + isShortCircuit := v.Flags.IsSet(validators.ShortCircuit) + isNonError := v.Flags.IsSet(validators.NonError) - fn, extraArgs := v.SignatureAndArgs() targs := generator.Args{ - "funcName": c.Universe.Type(fn), + "funcName": c.Universe.Type(v.Function), "field": mkSymbolArgs(c, fieldPkgSymbols), } emitCall := func() { sw.Do("$.funcName|raw$", targs) - typeArgs := v.TypeArgs() - if len(typeArgs) > 0 { + if typeArgs := v.TypeArgs; len(typeArgs) > 0 { sw.Do("[", nil) for i, typeArg := range typeArgs { sw.Do("$.|raw$", c.Universe.Type(typeArg)) @@ -1139,7 +1137,7 @@ func emitCallsToValidators(c *generator.Context, validations []validators.Functi sw.Do("]", nil) } sw.Do("(ctx, op, fldPath, obj, oldObj", targs) - for _, arg := range extraArgs { + for _, arg := range v.Args { sw.Do(", ", nil) toGolangSourceDataLiteral(sw, c, arg) } @@ -1147,21 +1145,21 @@ func emitCallsToValidators(c *generator.Context, validations []validators.Functi } // If validation is conditional, wrap the validation function with a conditions check. - if !v.Conditions().Empty() { + if !v.Conditions.Empty() { emitBaseFunction := emitCall emitCall = func() { sw.Do("func() $.field.ErrorList|raw$ {\n", targs) sw.Do(" if ", nil) firstCondition := true - if len(v.Conditions().OptionEnabled) > 0 { - sw.Do("op.Options.Has($.$)", strconv.Quote(v.Conditions().OptionEnabled)) + if len(v.Conditions.OptionEnabled) > 0 { + sw.Do("op.Options.Has($.$)", strconv.Quote(v.Conditions.OptionEnabled)) firstCondition = false } - if len(v.Conditions().OptionDisabled) > 0 { + if len(v.Conditions.OptionDisabled) > 0 { if !firstCondition { sw.Do(" && ", nil) } - sw.Do("!op.Options.Has($.$)", strconv.Quote(v.Conditions().OptionDisabled)) + sw.Do("!op.Options.Has($.$)", strconv.Quote(v.Conditions.OptionDisabled)) } sw.Do(" {\n", nil) sw.Do(" return ", nil) @@ -1174,7 +1172,7 @@ func emitCallsToValidators(c *generator.Context, validations []validators.Functi } } - for _, comment := range v.Comments() { + for _, comment := range v.Comments { sw.Do("// $.$\n", comment) } if isShortCircuit { @@ -1218,16 +1216,15 @@ func (g *genValidations) emitValidationVariables(c *generator.Context, t *types. }) for _, variable := range variables { fn := variable.Init() - supportInitFn, supportInitArgs := fn.SignatureAndArgs() targs := generator.Args{ "varName": c.Universe.Type(types.Name(variable.Var())), - "initFn": c.Universe.Type(supportInitFn), + "initFn": c.Universe.Type(fn.Function), } - for _, comment := range fn.Comments() { + for _, comment := range fn.Comments { sw.Do("// $.$\n", comment) } sw.Do("var $.varName|private$ = $.initFn|raw$", targs) - typeArgs := variable.Init().TypeArgs() + typeArgs := variable.Init().TypeArgs if len(typeArgs) > 0 { sw.Do("[", nil) for i, typeArg := range typeArgs { @@ -1239,11 +1236,11 @@ func (g *genValidations) emitValidationVariables(c *generator.Context, t *types. sw.Do("]", nil) } sw.Do("(", targs) - for i, arg := range supportInitArgs { - toGolangSourceDataLiteral(sw, c, arg) - if i < len(supportInitArgs)-1 { + for i, arg := range fn.Args { + if i != 0 { sw.Do(", ", nil) } + toGolangSourceDataLiteral(sw, c, arg) } sw.Do(")\n", nil) @@ -1277,14 +1274,13 @@ func toGolangSourceDataLiteral(sw *generator.SnippetWriter, c *generator.Context case *validators.PrivateVar: sw.Do("$.|private$", c.Universe.Type(types.Name(*v))) case validators.WrapperFunction: - fn, extraArgs := v.Function.SignatureAndArgs() - if len(extraArgs) == 0 { + if extraArgs := v.Function.Args; len(extraArgs) == 0 { // If the function to be wrapped has no additional arguments, we can // just use it directly. targs := generator.Args{ - "funcName": c.Universe.Type(fn), + "funcName": c.Universe.Type(v.Function.Function), } - for _, comment := range v.Function.Comments() { + for _, comment := range v.Function.Comments { sw.Do("// $.$\n", comment) } sw.Do("$.funcName|raw$", targs) @@ -1292,7 +1288,7 @@ func toGolangSourceDataLiteral(sw *generator.SnippetWriter, c *generator.Context // If the function to be wrapped has additional arguments, we need // a "standard signature" validation function to wrap it. targs := generator.Args{ - "funcName": c.Universe.Type(fn), + "funcName": c.Universe.Type(v.Function.Function), "field": mkSymbolArgs(c, fieldPkgSymbols), "operation": mkSymbolArgs(c, operationPkgSymbols), "context": mkSymbolArgs(c, contextPkgSymbols), @@ -1305,7 +1301,7 @@ func toGolangSourceDataLiteral(sw *generator.SnippetWriter, c *generator.Context emitCall := func() { sw.Do("return $.funcName|raw$", targs) - typeArgs := v.Function.TypeArgs() + typeArgs := v.Function.TypeArgs if len(typeArgs) > 0 { sw.Do("[", nil) for i, typeArg := range typeArgs { diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/each.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/each.go index 35e6a2eccc0..38159faea2e 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/each.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/each.go @@ -250,8 +250,8 @@ func (evtv eachValTagValidator) getValidations(fldPath *field.Path, t *types.Typ // ForEachVal returns a validation that applies a function to each element of // a list or map. -func ForEachVal(fldPath *field.Path, t *types.Type, fn FunctionGen) (Validations, error) { - return globalEachVal.getValidations(fldPath, t, Validations{Functions: []FunctionGen{fn}}) +func ForEachVal(fldPath *field.Path, t *types.Type, fn *FunctionGen) (Validations, error) { + return globalEachVal.getValidations(fldPath, t, Validations{Functions: []*FunctionGen{fn}}) } func (evtv eachValTagValidator) getListValidations(fldPath *field.Path, t *types.Type, validations Validations) (Validations, error) { @@ -286,7 +286,7 @@ func (evtv eachValTagValidator) getListValidations(fldPath *field.Path, t *types cmpFn.Body = buf.String() cmpArg = cmpFn } - f := Function(eachValTagName, vfn.Flags(), validateEachSliceVal, cmpArg, WrapperFunction{vfn, t.Elem}) + f := Function(eachValTagName, vfn.Flags, validateEachSliceVal, cmpArg, WrapperFunction{vfn, t.Elem}) result.Functions = append(result.Functions, f) } @@ -298,7 +298,7 @@ func (evtv eachValTagValidator) getMapValidations(t *types.Type, validations Val result.OpaqueValType = validations.OpaqueType for _, vfn := range validations.Functions { - f := Function(eachValTagName, vfn.Flags(), validateEachMapVal, WrapperFunction{vfn, t.Elem}) + f := Function(eachValTagName, vfn.Flags, validateEachMapVal, WrapperFunction{vfn, t.Elem}) result.Functions = append(result.Functions, f) } @@ -369,7 +369,7 @@ func (ektv eachKeyTagValidator) getValidations(t *types.Type, validations Valida result := Validations{} result.OpaqueKeyType = validations.OpaqueType for _, vfn := range validations.Functions { - f := Function(eachKeyTagName, vfn.Flags(), validateEachMapKey, WrapperFunction{vfn, t.Key}) + f := Function(eachKeyTagName, vfn.Flags, validateEachMapKey, WrapperFunction{vfn, t.Key}) result.Functions = append(result.Functions, f) } return result, nil @@ -377,8 +377,8 @@ func (ektv eachKeyTagValidator) getValidations(t *types.Type, validations Valida // ForEachKey returns a validation that applies a function to each key of // a map. -func ForEachKey(_ *field.Path, t *types.Type, fn FunctionGen) (Validations, error) { - return globalEachKey.getValidations(t, Validations{Functions: []FunctionGen{fn}}) +func ForEachKey(_ *field.Path, t *types.Type, fn *FunctionGen) (Validations, error) { + return globalEachKey.getValidations(t, Validations{Functions: []*FunctionGen{fn}}) } func (ektv eachKeyTagValidator) Docs() TagDoc { diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/registry.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/registry.go index 1fabcf3833a..537a3c05071 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/registry.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/registry.go @@ -165,7 +165,7 @@ func (reg *registry) sortTagsIntoPhases(tags map[string][]gengo.Tag) [][]string // Tag extraction will retain the relative order between 111 and 222, but // 333 is extracted as tag "k8s:ifOptionEnabled". Those are all in a map, // which we iterate (in a random order). When it reaches the emit stage, - // the "ifOptionEnabled" part is gone, and we will have 3 functionGen + // the "ifOptionEnabled" part is gone, and we will have 3 FunctionGen // objects, all with tag "k8s:validateFalse", in a non-deterministic order // because of the map iteration. If we sort them at that point, we won't // have enough information to do something smart, unless we look at the diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/required.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/required.go index 54d7fd44a05..c50b36d1ecd 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/required.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/required.go @@ -96,11 +96,11 @@ func (rtv requirednessTagValidator) doRequired(context Context) (Validations, er // do manual dispatch here. switch context.Type.Kind { case types.Slice: - return Validations{Functions: []FunctionGen{Function(requiredTagName, ShortCircuit, requiredSliceValidator)}}, nil + return Validations{Functions: []*FunctionGen{Function(requiredTagName, ShortCircuit, requiredSliceValidator)}}, nil case types.Map: - return Validations{Functions: []FunctionGen{Function(requiredTagName, ShortCircuit, requiredMapValidator)}}, nil + return Validations{Functions: []*FunctionGen{Function(requiredTagName, ShortCircuit, requiredMapValidator)}}, nil case types.Pointer: - return Validations{Functions: []FunctionGen{Function(requiredTagName, ShortCircuit, requiredPointerValidator)}}, nil + return Validations{Functions: []*FunctionGen{Function(requiredTagName, ShortCircuit, requiredPointerValidator)}}, nil case types.Struct: // The +k8s:required tag on a non-pointer struct is not supported. // If you encounter this error and believe you have a valid use case @@ -109,7 +109,7 @@ func (rtv requirednessTagValidator) doRequired(context Context) (Validations, er // this behavior or provide alternative validation mechanisms. return Validations{}, fmt.Errorf("non-pointer structs cannot use the %q tag", requiredTagName) } - return Validations{Functions: []FunctionGen{Function(requiredTagName, ShortCircuit, requiredValueValidator)}}, nil + return Validations{Functions: []*FunctionGen{Function(requiredTagName, ShortCircuit, requiredValueValidator)}}, nil } var ( @@ -167,11 +167,11 @@ func (rtv requirednessTagValidator) doOptional(context Context) (Validations, er // do manual dispatch here. switch context.Type.Kind { case types.Slice: - return Validations{Functions: []FunctionGen{Function(optionalTagName, ShortCircuit|NonError, optionalSliceValidator)}}, nil + return Validations{Functions: []*FunctionGen{Function(optionalTagName, ShortCircuit|NonError, optionalSliceValidator)}}, nil case types.Map: - return Validations{Functions: []FunctionGen{Function(optionalTagName, ShortCircuit|NonError, optionalMapValidator)}}, nil + return Validations{Functions: []*FunctionGen{Function(optionalTagName, ShortCircuit|NonError, optionalMapValidator)}}, nil case types.Pointer: - return Validations{Functions: []FunctionGen{Function(optionalTagName, ShortCircuit|NonError, optionalPointerValidator)}}, nil + return Validations{Functions: []*FunctionGen{Function(optionalTagName, ShortCircuit|NonError, optionalPointerValidator)}}, nil case types.Struct: // The +k8s:optional tag on a non-pointer struct is not supported. // If you encounter this error and believe you have a valid use case @@ -180,7 +180,7 @@ func (rtv requirednessTagValidator) doOptional(context Context) (Validations, er // this behavior or provide alternative validation mechanisms. return Validations{}, fmt.Errorf("non-pointer structs cannot use the %q tag", optionalTagName) } - return Validations{Functions: []FunctionGen{Function(optionalTagName, ShortCircuit|NonError, optionalValueValidator)}}, nil + return Validations{Functions: []*FunctionGen{Function(optionalTagName, ShortCircuit|NonError, optionalValueValidator)}}, nil } // hasZeroDefault returns whether the field has a default value and whether @@ -268,21 +268,21 @@ func (requirednessTagValidator) doForbidden(context Context) (Validations, error switch context.Type.Kind { case types.Slice: return Validations{ - Functions: []FunctionGen{ + Functions: []*FunctionGen{ Function(forbiddenTagName, ShortCircuit, forbiddenSliceValidator), Function(forbiddenTagName, ShortCircuit|NonError, optionalSliceValidator), }, }, nil case types.Map: return Validations{ - Functions: []FunctionGen{ + Functions: []*FunctionGen{ Function(forbiddenTagName, ShortCircuit, forbiddenMapValidator), Function(forbiddenTagName, ShortCircuit|NonError, optionalMapValidator), }, }, nil case types.Pointer: return Validations{ - Functions: []FunctionGen{ + Functions: []*FunctionGen{ Function(forbiddenTagName, ShortCircuit, forbiddenPointerValidator), Function(forbiddenTagName, ShortCircuit|NonError, optionalPointerValidator), }, @@ -296,7 +296,7 @@ func (requirednessTagValidator) doForbidden(context Context) (Validations, error return Validations{}, fmt.Errorf("non-pointer structs cannot use the %q tag", forbiddenTagName) } return Validations{ - Functions: []FunctionGen{ + Functions: []*FunctionGen{ Function(forbiddenTagName, ShortCircuit, forbiddenValueValidator), Function(forbiddenTagName, ShortCircuit|NonError, optionalValueValidator), }, diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/subfield.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/subfield.go index f6913b19c20..30f3aba44d3 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/subfield.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/subfield.go @@ -100,7 +100,7 @@ func (stv subfieldTagValidator) GetValidations(context Context, args []string, p Results: []ParamResult{{"", nilableFieldType}}, } getFn.Body = fmt.Sprintf("return %so.%s", fieldExprPrefix, submemb.Name) - f := Function(subfieldTagName, vfn.Flags(), validateSubfield, subname, getFn, WrapperFunction{vfn, submemb.Type}) + f := Function(subfieldTagName, vfn.Flags, validateSubfield, subname, getFn, WrapperFunction{vfn, submemb.Type}) result.Functions = append(result.Functions, f) result.Variables = append(result.Variables, validations.Variables...) } diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/testing.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/testing.go index 92769f1c29b..c50f1e07a3f 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/testing.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/testing.go @@ -73,7 +73,7 @@ func (frtv fixedResultTagValidator) GetValidations(context Context, _ []string, if err != nil { return result, fmt.Errorf("can't decode tag payload: %w", err) } - result.AddFunction(GenericFunction(frtv.TagName(), tag.flags, fixedResultValidator, tag.typeArgs, frtv.result, tag.msg)) + result.AddFunction(Function(frtv.TagName(), tag.flags, fixedResultValidator, frtv.result, tag.msg).WithTypeArgs(tag.typeArgs...)) return result, nil } diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/validators.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/validators.go index 5d460ea5f6b..789e91b73b2 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/validators.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/validators.go @@ -207,13 +207,47 @@ type TagPayloadSchema struct { Default string } -// Validations defines the function calls and variables to generate to perform validation. +// Validations defines the function calls and variables to generate to perform +// validation. type Validations struct { - Functions []FunctionGen - Variables []VariableGen - Comments []string - OpaqueType bool + // Functions holds the function calls that should be generated to perform + // validation. These functions may not be called in order - they may be + // sorted based on their flags and other criteria. + // + // Each function's signature must be of the form: + // func( + // // standard arguments + // ctx context.Context + // op operation.Operation, + // fldPath field.Path, + // value, oldValue , // always nilable + // // additional arguments (optional) + // Args[0] , + // Args[1] , + // ... + // Args[N] ) + // + // The standard arguments are not included in the FunctionGen.Args list. + Functions []*FunctionGen + + // Variables holds any variables which must be generated to perform + // validation. Variables are not permitted in every context. + Variables []VariableGen + + // Comments holds comments to emit (without the leanding "//"). + Comments []string + + // OpaqueType indicates that the type being validated is opaque, and that + // any validations defined on it should not be emitted. + OpaqueType bool + + // OpaqueKeyType indicates that the key type of a map being validated is + // opaque, and that any validations defined on it should not be emitted. OpaqueKeyType bool + + // OpaqueValType indicates that the key type of a map or slice being + // validated is opaque, and that any validations defined on it should not + // be emitted. OpaqueValType bool } @@ -225,7 +259,7 @@ func (v *Validations) Len() int { return len(v.Functions) + len(v.Variables) + len(v.Comments) } -func (v *Validations) AddFunction(f FunctionGen) { +func (v *Validations) AddFunction(f *FunctionGen) { v.Functions = append(v.Functions, f) } @@ -269,46 +303,6 @@ const ( NonError ) -// FunctionGen provides validation-gen with the information needed to generate a -// validation function invocation. -type FunctionGen interface { - // TagName returns the tag which triggers this validator. - TagName() string - - // SignatureAndArgs returns the function name and all extraArg value literals that are passed when the function - // invocation is generated. - // - // The function signature must be of the form: - // func(op operation.Operation, - // fldPath field.Path, - // value, oldValue , // always nilable - // extraArgs[0] , // optional - // ..., - // extraArgs[N] ) - // - // extraArgs may contain: - // - data literals comprised of maps, slices, strings, ints, floats and bools - // - references, represented by types.Type (to reference any type in the universe), and types.Member (to reference members of the current value) - // - // If validation function to be called does not have a signature of this form, please introduce - // a function that does and use that function to call the validation function. - SignatureAndArgs() (function types.Name, extraArgs []any) - - // TypeArgs assigns types to the type parameters of the function, for invocation. - TypeArgs() []types.Name - - // Flags returns the options for this validator function. - Flags() FunctionFlags - - // Conditions returns the conditions that must true for a resource to be - // validated by this function. - Conditions() Conditions - - // Comments returns optional comments that should be added to the generated - // code (without the leading "//"). - Comments() []string -} - // Conditions defines what conditions must be true for a resource to be validated. // If any of the conditions are not true, the resource is not validated. type Conditions struct { @@ -342,83 +336,76 @@ type VariableGen interface { Var() PrivateVar // Init generates the function call that the variable is assigned to. - Init() FunctionGen + Init() *FunctionGen } // Function creates a FunctionGen for a given function name and extraArgs. -func Function(tagName string, flags FunctionFlags, function types.Name, extraArgs ...any) FunctionGen { - return GenericFunction(tagName, flags, function, nil, extraArgs...) -} - -func GenericFunction(tagName string, flags FunctionFlags, function types.Name, typeArgs []types.Name, extraArgs ...any) FunctionGen { - // Callers of Signature don't care if the args are all of a known type, it just - // makes it easier to declare validators. - var anyArgs []any - if len(extraArgs) > 0 { - anyArgs = make([]any, len(extraArgs)) - copy(anyArgs, extraArgs) - } - return &functionGen{tagName: tagName, flags: flags, function: function, extraArgs: anyArgs, typeArgs: typeArgs} -} - -// WithCondition adds a condition to a FunctionGen. -func WithCondition(fn FunctionGen, conditions Conditions) FunctionGen { - name, args := fn.SignatureAndArgs() - return &functionGen{ - tagName: fn.TagName(), - flags: fn.Flags(), - function: name, - extraArgs: args, - typeArgs: fn.TypeArgs(), - comments: fn.Comments(), - conditions: conditions, +func Function(tagName string, flags FunctionFlags, function types.Name, extraArgs ...any) *FunctionGen { + return &FunctionGen{ + TagName: tagName, + Flags: flags, + Function: function, + Args: extraArgs, } } -// WithComment adds a comment to a FunctionGen. -func WithComment(fn FunctionGen, comment string) FunctionGen { - name, args := fn.SignatureAndArgs() - return &functionGen{ - tagName: fn.TagName(), - flags: fn.Flags(), - function: name, - extraArgs: args, - typeArgs: fn.TypeArgs(), - comments: append(fn.Comments(), comment), - conditions: fn.Conditions(), - } +// FunctionGen describes a function call that should be generated. +type FunctionGen struct { + // TagName is the tag which triggered this function. + TagName string + + // Flags holds the options for this validator function. + Flags FunctionFlags + + // Function is the name of the function to call. + Function types.Name + + // Args holds arguments to pass to the function, and may conatin: + // - data literals comprised of maps, slices, strings, ints, floats, and bools + // - types.Type (to reference any type in the universe) + // - types.Member (to reference members of the current value) + // - types.Identifier (to reference any identifier in the universe) + // - validators.WrapperFunction (to call another validation function) + // - validators.Literal (to pass a literal value) + // - validators.FunctionLiteral (to pass a function literal) + // - validators.PrivateVar (to reference a variable) + // + // See toGolangSourceDataLiteral for details. + Args []any + + // TypeArgs assigns types to the type parameters of the function, for + // generic function calls which require explicit type arguments. + TypeArgs []types.Name + + // Conditions holds any conditions that must true for a field to be + // validated by this function. + Conditions Conditions + + // Comments holds optional comments that should be added to the generated + // code (without the leading "//"). + Comments []string } -type functionGen struct { - tagName string - function types.Name - extraArgs []any - typeArgs []types.Name - flags FunctionFlags - conditions Conditions - comments []string +// WithTypeArgs sets the type arguments for a FunctionGen. +func (fg *FunctionGen) WithTypeArgs(typeArgs ...types.Name) *FunctionGen { + fg.TypeArgs = typeArgs + return fg } -func (v *functionGen) TagName() string { - return v.tagName +// WithConditions sets the conditions for a FunctionGen. +func (fg *FunctionGen) WithConditions(conditions Conditions) *FunctionGen { + fg.Conditions = conditions + return fg } -func (v *functionGen) SignatureAndArgs() (function types.Name, args []any) { - return v.function, v.extraArgs +// AddComment adds a comment to a FunctionGen. +func (fg *FunctionGen) AddComment(comment string) *FunctionGen { + fg.Comments = append(fg.Comments, comment) + return fg } -func (v *functionGen) TypeArgs() []types.Name { return v.typeArgs } - -func (v *functionGen) Flags() FunctionFlags { - return v.flags -} - -func (v *functionGen) Conditions() Conditions { return v.conditions } - -func (v *functionGen) Comments() []string { return v.comments } - // Variable creates a VariableGen for a given function name and extraArgs. -func Variable(variable PrivateVar, init FunctionGen) VariableGen { +func Variable(variable PrivateVar, init *FunctionGen) VariableGen { return &variableGen{ variable: variable, init: init, @@ -427,18 +414,18 @@ func Variable(variable PrivateVar, init FunctionGen) VariableGen { type variableGen struct { variable PrivateVar - init FunctionGen + init *FunctionGen } func (v variableGen) TagName() string { - return v.init.TagName() + return v.init.TagName } func (v variableGen) Var() PrivateVar { return v.variable } -func (v variableGen) Init() FunctionGen { +func (v variableGen) Init() *FunctionGen { return v.init } @@ -446,7 +433,7 @@ func (v variableGen) Init() FunctionGen { // regular validation function (op, fldPath, obj, oldObj) and calls another // validation function with the same signature, plus extra args if needed. type WrapperFunction struct { - Function FunctionGen + Function *FunctionGen ObjType *types.Type }