mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-07-28 01:34:19 +00:00
Add constant discovery, ScopeConst and StructLiberal. Remove ScopeAny.
# Conflicts: # staging/src/k8s.io/code-generator/cmd/validation-gen/validators/limits.go # Conflicts: # staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/options/zz_generated.validations.go
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright The Kubernetes 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.
|
||||
*/
|
||||
|
||||
// Code generated by validation-gen. DO NOT EDIT.
|
||||
|
||||
package options
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
|
||||
operation "k8s.io/apimachinery/pkg/api/operation"
|
||||
safe "k8s.io/apimachinery/pkg/api/safe"
|
||||
validate "k8s.io/apimachinery/pkg/api/validate"
|
||||
field "k8s.io/apimachinery/pkg/util/validation/field"
|
||||
testscheme "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
)
|
||||
|
||||
func init() { localSchemeBuilder.Register(RegisterValidations) }
|
||||
|
||||
// RegisterValidations adds validation functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
func RegisterValidations(scheme *testscheme.Scheme) error {
|
||||
scheme.AddValidationFunc((*Struct)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_Struct(ctx, op, nil /* fldPath */, obj.(*Struct), safe.Cast[*Struct](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) {
|
||||
// field Struct.TypeMeta has no validation
|
||||
|
||||
// field Struct.ObjectMeta
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *ObjectMeta) (errs field.ErrorList) {
|
||||
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
|
||||
return nil // no changes
|
||||
}
|
||||
errs = append(errs, validate.IfOption(ctx, op, fldPath, obj, oldObj, "FeatureX", true, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *ObjectMeta) field.ErrorList {
|
||||
return validate.Subfield(ctx, op, fldPath, obj, oldObj, "xEnabledField", func(o *ObjectMeta) *string { return &o.XEnabledField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ObjectMeta.XEnabledField")
|
||||
})
|
||||
})...)
|
||||
return
|
||||
}(fldPath.Child("metadata"), &obj.ObjectMeta, safe.Field(oldObj, func(oldObj *Struct) *ObjectMeta { return &oldObj.ObjectMeta }))...)
|
||||
|
||||
// field Struct.XEnabledField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) {
|
||||
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
|
||||
return nil // no changes
|
||||
}
|
||||
errs = append(errs, validate.IfOption(ctx, op, fldPath, obj, oldObj, "FeatureX", true, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.XEnabledField")
|
||||
})...)
|
||||
return
|
||||
}(fldPath.Child("xEnabledField"), &obj.XEnabledField, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.XEnabledField }))...)
|
||||
|
||||
// field Struct.XDisabledField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) {
|
||||
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
|
||||
return nil // no changes
|
||||
}
|
||||
errs = append(errs, validate.IfOption(ctx, op, fldPath, obj, oldObj, "FeatureX", false, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.XDisabledField")
|
||||
})...)
|
||||
return
|
||||
}(fldPath.Child("xDisabledField"), &obj.XDisabledField, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.XDisabledField }))...)
|
||||
|
||||
// field Struct.YEnabledField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) {
|
||||
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
|
||||
return nil // no changes
|
||||
}
|
||||
errs = append(errs, validate.IfOption(ctx, op, fldPath, obj, oldObj, "FeatureY", true, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.YEnabledField")
|
||||
})...)
|
||||
return
|
||||
}(fldPath.Child("yEnabledField"), &obj.YEnabledField, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.YEnabledField }))...)
|
||||
|
||||
// field Struct.YDisabledField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) {
|
||||
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
|
||||
return nil // no changes
|
||||
}
|
||||
errs = append(errs, validate.IfOption(ctx, op, fldPath, obj, oldObj, "FeatureY", false, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.YDisabledField")
|
||||
})...)
|
||||
return
|
||||
}(fldPath.Child("yDisabledField"), &obj.YDisabledField, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.YDisabledField }))...)
|
||||
|
||||
// field Struct.XYMixedField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) {
|
||||
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
|
||||
return nil // no changes
|
||||
}
|
||||
errs = append(errs, validate.IfOption(ctx, op, fldPath, obj, oldObj, "FeatureY", false, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.XYMixedField/Y")
|
||||
})...)
|
||||
errs = append(errs, validate.IfOption(ctx, op, fldPath, obj, oldObj, "FeatureX", true, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.XYMixedField/X")
|
||||
})...)
|
||||
return
|
||||
}(fldPath.Child("xyMixedField"), &obj.XYMixedField, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.XYMixedField }))...)
|
||||
|
||||
return errs
|
||||
}
|
||||
@@ -287,6 +287,9 @@ func GetTargets(context *generator.Context, args *Args) []generator.Target {
|
||||
|
||||
// Create a type discoverer for all types of all inputs.
|
||||
td := NewTypeDiscoverer(validator, inputToPkg)
|
||||
if err := td.Init(context); err != nil {
|
||||
klog.Fatalf("Error discovering constants: %v", err)
|
||||
}
|
||||
|
||||
// Create a linter to collect errors as we go.
|
||||
linter := newLinter()
|
||||
|
||||
@@ -148,23 +148,62 @@ func (g *genValidations) GenerateType(c *generator.Context, t *types.Type, w io.
|
||||
|
||||
// typeDiscoverer contains fields necessary to build graphs of types.
|
||||
type typeDiscoverer struct {
|
||||
validator validators.Validator
|
||||
inputToPkg map[string]string
|
||||
initialized bool
|
||||
validator validators.Validator
|
||||
inputToPkg map[string]string
|
||||
|
||||
// constantsByType holds a map of type to constants of that type.
|
||||
constantsByType map[*types.Type][]*validators.Constant
|
||||
|
||||
// typeNodes holds a map of gengo Type to typeNode for all of the types
|
||||
// encountered during discovery.
|
||||
typeNodes map[*types.Type]*typeNode
|
||||
}
|
||||
|
||||
// NewTypeDiscoverer creates and initializes a NewTypeDiscoverer.
|
||||
// NewTypeDiscoverer creates a NewTypeDiscoverer.
|
||||
// Init must be called before calling DiscoverType.
|
||||
func NewTypeDiscoverer(validator validators.Validator, inputToPkg map[string]string) *typeDiscoverer {
|
||||
return &typeDiscoverer{
|
||||
validator: validator,
|
||||
inputToPkg: inputToPkg,
|
||||
typeNodes: map[*types.Type]*typeNode{},
|
||||
validator: validator,
|
||||
inputToPkg: inputToPkg,
|
||||
constantsByType: map[*types.Type][]*validators.Constant{},
|
||||
typeNodes: map[*types.Type]*typeNode{},
|
||||
}
|
||||
}
|
||||
|
||||
// Init uses the generator context to prepare for type discovery.
|
||||
func (td *typeDiscoverer) Init(c *generator.Context) error {
|
||||
packages := c.Universe
|
||||
for _, pkg := range packages {
|
||||
// We only care about packages we are generating for or are readonly.
|
||||
if _, ok := td.inputToPkg[pkg.Path]; !ok {
|
||||
continue
|
||||
}
|
||||
for _, cnst := range pkg.Constants {
|
||||
context := validators.Context{
|
||||
Scope: validators.ScopeConst,
|
||||
Type: cnst.Underlying,
|
||||
Path: nil, // NA when discovering a constant
|
||||
Member: nil, // NA when discovering a constant
|
||||
ParentPath: nil, // NA when discovering a constant
|
||||
}
|
||||
tgs, err := td.validator.ExtractTags(context, cnst.CommentLines)
|
||||
if err != nil {
|
||||
return fmt.Errorf("constant %s: %w", cnst.Name, err)
|
||||
}
|
||||
if len(tgs) > 0 {
|
||||
// Also check that the tgs are valid.
|
||||
if _, err := td.validator.ExtractValidations(context, tgs...); err != nil {
|
||||
return fmt.Errorf("constant %s: %w", cnst.Name, err)
|
||||
}
|
||||
}
|
||||
td.constantsByType[cnst.Underlying] = append(td.constantsByType[cnst.Underlying], &validators.Constant{Constant: cnst, Tags: tgs})
|
||||
}
|
||||
}
|
||||
td.initialized = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// childNode represents a type which is used in another type (e.g. a struct
|
||||
// field).
|
||||
type childNode struct {
|
||||
@@ -210,6 +249,9 @@ type typeNode struct {
|
||||
// typeDiscoverer. If this is called multiple times for different types, the
|
||||
// graphs will be will be merged.
|
||||
func (td *typeDiscoverer) DiscoverType(t *types.Type) error {
|
||||
if !td.initialized {
|
||||
return fmt.Errorf("typeDiscoverer not initialized")
|
||||
}
|
||||
if t.Kind == types.Pointer {
|
||||
return fmt.Errorf("type %v: pointer root-types are not supported", t)
|
||||
}
|
||||
@@ -349,11 +391,14 @@ func (td *typeDiscoverer) discoverType(t *types.Type, fldPath *field.Path) (*typ
|
||||
if fldPath.String() != t.String() {
|
||||
panic(fmt.Sprintf("path for type != the type name: %s, %s", t.String(), fldPath.String()))
|
||||
}
|
||||
consts, _ := td.constantsByType[t]
|
||||
context := validators.Context{
|
||||
Scope: validators.ScopeType,
|
||||
Type: t,
|
||||
ParentPath: nil,
|
||||
Path: fldPath,
|
||||
Member: nil, // NA when discovering a type
|
||||
ParentPath: nil, // NA when discovering a type
|
||||
Constants: consts,
|
||||
}
|
||||
extractedTags, err := td.validator.ExtractTags(context, t.CommentLines)
|
||||
if err != nil {
|
||||
@@ -1440,6 +1485,29 @@ func toGolangSourceDataLiteral(sw *generator.SnippetWriter, c *generator.Context
|
||||
sw.Do(")", nil)
|
||||
}
|
||||
sw.Do(" { $.$ }", v.Body)
|
||||
case validators.StructLiteral:
|
||||
targs := generator.Args{
|
||||
"type": c.Universe.Type(v.Type),
|
||||
}
|
||||
sw.Do("$.type|raw$", targs)
|
||||
if len(v.TypeArgs) > 0 {
|
||||
sw.Do("[", nil)
|
||||
for i, typeArg := range v.TypeArgs {
|
||||
if i > 0 {
|
||||
sw.Do(", ", nil)
|
||||
}
|
||||
sw.Do("$.|raw$", typeArg)
|
||||
}
|
||||
sw.Do("]", nil)
|
||||
}
|
||||
sw.Do("{\n", nil)
|
||||
for _, f := range v.Fields {
|
||||
sw.Do(f.Name, nil)
|
||||
sw.Do(": ", nil)
|
||||
toGolangSourceDataLiteral(sw, c, f.Value)
|
||||
sw.Do(", ", nil)
|
||||
}
|
||||
sw.Do("}", targs)
|
||||
default:
|
||||
rv := reflect.ValueOf(value)
|
||||
switch rv.Kind() {
|
||||
|
||||
@@ -81,7 +81,7 @@ func init() {
|
||||
}
|
||||
|
||||
// This applies to all tags in this file.
|
||||
var listTagsValidScopes = sets.New(ScopeAny)
|
||||
var listTagsValidScopes = sets.New(ScopeType, ScopeField, ScopeListVal, ScopeMapKey, ScopeMapVal)
|
||||
|
||||
// listMetadata collects information about a single list with map or set semantics.
|
||||
type listMetadata struct {
|
||||
|
||||
@@ -42,7 +42,7 @@ func (neqTagValidator) TagName() string {
|
||||
return neqTagName
|
||||
}
|
||||
|
||||
var neqTagValidScopes = sets.New(ScopeAny)
|
||||
var neqTagValidScopes = sets.New(ScopeType, ScopeField, ScopeListVal, ScopeMapKey, ScopeMapVal)
|
||||
|
||||
func (neqTagValidator) ValidScopes() sets.Set[Scope] {
|
||||
return neqTagValidScopes
|
||||
|
||||
@@ -58,7 +58,7 @@ func (itemTagValidator) TagName() string {
|
||||
return itemTagName
|
||||
}
|
||||
|
||||
var itemTagValidScopes = sets.New(ScopeAny)
|
||||
var itemTagValidScopes = sets.New(ScopeType, ScopeField, ScopeListVal, ScopeMapKey, ScopeMapVal)
|
||||
|
||||
func (itemTagValidator) ValidScopes() sets.Set[Scope] {
|
||||
return itemTagValidScopes
|
||||
|
||||
@@ -42,9 +42,7 @@ func (minimumTagValidator) TagName() string {
|
||||
return minimumTagName
|
||||
}
|
||||
|
||||
var minimumTagValidScopes = sets.New(
|
||||
ScopeAny,
|
||||
)
|
||||
var minimumTagValidScopes = sets.New(ScopeType, ScopeField, ScopeListVal, ScopeMapKey, ScopeMapVal)
|
||||
|
||||
func (minimumTagValidator) ValidScopes() sets.Set[Scope] {
|
||||
return minimumTagValidScopes
|
||||
|
||||
@@ -38,7 +38,7 @@ func (opaqueTypeTagValidator) TagName() string {
|
||||
}
|
||||
|
||||
func (opaqueTypeTagValidator) ValidScopes() sets.Set[Scope] {
|
||||
return sets.New(ScopeAny)
|
||||
return sets.New(ScopeType, ScopeField, ScopeListVal, ScopeMapKey, ScopeMapVal)
|
||||
}
|
||||
|
||||
func (opaqueTypeTagValidator) GetValidations(_ Context, _ codetags.Tag) (Validations, error) {
|
||||
|
||||
@@ -152,7 +152,8 @@ func (reg *registry) ExtractValidations(context Context, tags ...codetags.Tag) (
|
||||
for _, tags := range phases {
|
||||
for _, tag := range tags {
|
||||
tv := reg.tagValidators[tag.Name]
|
||||
if scopes := tv.ValidScopes(); !scopes.Has(context.Scope) && !scopes.Has(ScopeAny) {
|
||||
// At this point we know tv exists and is not nil due to the upfront check
|
||||
if scopes := tv.ValidScopes(); !scopes.Has(context.Scope) {
|
||||
return Validations{}, fmt.Errorf("tag %q cannot be specified on %s", tv.TagName(), context.Scope)
|
||||
}
|
||||
if err := typeCheck(tag, tv.Docs()); err != nil {
|
||||
|
||||
@@ -45,7 +45,7 @@ func (subfieldTagValidator) TagName() string {
|
||||
return subfieldTagName
|
||||
}
|
||||
|
||||
var subfieldTagValidScopes = sets.New(ScopeAny)
|
||||
var subfieldTagValidScopes = sets.New(ScopeType, ScopeField, ScopeListVal, ScopeMapKey, ScopeMapVal)
|
||||
|
||||
func (subfieldTagValidator) ValidScopes() sets.Set[Scope] {
|
||||
return subfieldTagValidScopes
|
||||
|
||||
@@ -56,7 +56,7 @@ func (frtv fixedResultTagValidator) TagName() string {
|
||||
return validateFalseTagName
|
||||
}
|
||||
|
||||
var fixedResultTagValidScopes = sets.New(ScopeAny)
|
||||
var fixedResultTagValidScopes = sets.New(ScopeType, ScopeField, ScopeListVal, ScopeMapKey, ScopeMapVal)
|
||||
|
||||
func (fixedResultTagValidator) ValidScopes() sets.Set[Scope] {
|
||||
return fixedResultTagValidScopes
|
||||
|
||||
@@ -152,11 +152,6 @@ type Scope string
|
||||
// Note: All of these values should be strings which can be used in an error
|
||||
// message such as "may not be used in %s".
|
||||
const (
|
||||
// ScopeAny indicates that a validator may be use in any context. This value
|
||||
// should never appear in a Context struct, since that indicates a
|
||||
// specific use.
|
||||
ScopeAny Scope = "anywhere"
|
||||
|
||||
// ScopeType indicates a validation on a type definition, which applies to
|
||||
// all instances of that type.
|
||||
ScopeType Scope = "type definitions"
|
||||
@@ -177,6 +172,9 @@ const (
|
||||
// field or type.
|
||||
ScopeMapVal Scope = "map values"
|
||||
|
||||
// ScopeConst indicates a validation which applies to constant values only.
|
||||
ScopeConst Scope = "constant values"
|
||||
|
||||
// TODO: It's not clear if we need to distinguish (e.g.) list values of
|
||||
// fields from list values of typedefs. We could make {type,field} be
|
||||
// orthogonal to {scalar, list, list-value, map, map-key, map-value} (and
|
||||
@@ -195,22 +193,58 @@ type Context struct {
|
||||
// this is the field's type (which may be a pointer, an alias, or both).
|
||||
// When Scope indicates a list-value, map-key, or map-value, this is the
|
||||
// type of that key or value (which, again, may be a pointer, and alias, or
|
||||
// both).
|
||||
// both). When Scope is ScopeConst this is the constant's type.
|
||||
Type *types.Type
|
||||
|
||||
// ParentPath provides the field path to the parent type or field, enabling
|
||||
// unique identification of validation contexts for the same type in
|
||||
// different locations.
|
||||
ParentPath *field.Path
|
||||
// Path provides a path to the type or field being validated. This is
|
||||
// useful for identifying an exact context, e.g. to track information
|
||||
// between related tags. When Scope is ScopeType, this is the Go package
|
||||
// path and type name (e.g. "k8s.io/api/core/v1.Pod"). When Scope is
|
||||
// ScopeField, this is the field path (e.g. "spec.containers[*].image").
|
||||
// When Scope indicates a list-value, map-key, or map-value, this is the
|
||||
// type or field path, as described above, with a suffix indicating
|
||||
// that it refers to the keys or values. For ScopeConst, this will be nil.
|
||||
Path *field.Path
|
||||
|
||||
// Member provides details about a field within a struct when Scope is
|
||||
// ScopeField. For all other values of Scope, this will be nil.
|
||||
Member *types.Member
|
||||
|
||||
// Path provides the field path to the type or field being validated. This
|
||||
// is useful for identifying an exact context, e.g. to track information
|
||||
// between related tags.
|
||||
Path *field.Path
|
||||
// ListSelector provides a list of key-value pairs that represent criteria
|
||||
// for selecting one or more items from a list. When Scope is
|
||||
// ScopeListVal, this will be non-nil. An empty selector means that
|
||||
// all items in the list should be selected. For all other values of
|
||||
// Scope, this will be nil.
|
||||
ListSelector []ListSelectorTerm
|
||||
|
||||
// ParentPath provides a path to the parent type or field of the object
|
||||
// being validated, when applicable. enabling unique identification of
|
||||
// validation contexts for the same type in different locations. When
|
||||
// Scope is ScopeField, this is the path to the containing struct type or
|
||||
// field (depending on where the validation tag was sepcified). When Scope
|
||||
// indicates a list-value, map-key, or map-value, this is the path to the
|
||||
// list or map type or field (depending on where the validation tag was
|
||||
// specified). When Scope is ScopeType, this is nil.
|
||||
ParentPath *field.Path
|
||||
|
||||
// Constants provides access to all constants of the type being
|
||||
// validated. Only set when Scope is ScopeType.
|
||||
Constants []*Constant
|
||||
}
|
||||
|
||||
// Constant represents a constant value.
|
||||
type Constant struct {
|
||||
Constant *types.Type
|
||||
Tags []codetags.Tag
|
||||
}
|
||||
|
||||
// ListSelectorTerm represents a field name and value pair.
|
||||
type ListSelectorTerm struct {
|
||||
// Field is the JSON name of the field to match.
|
||||
Field string
|
||||
// Value is the value to match. This must be a primitive type which can
|
||||
// be used as list-map keys: string, int, or bool.
|
||||
Value any
|
||||
}
|
||||
|
||||
// TagDoc describes a comment-tag and its usage.
|
||||
@@ -499,6 +533,21 @@ type FunctionLiteral struct {
|
||||
Body string
|
||||
}
|
||||
|
||||
// StructLiteral represents a struct literal expression that can be used as
|
||||
// an argument to a validator.
|
||||
type StructLiteral struct {
|
||||
// Type is the type of the struct literal to be generated.
|
||||
Type types.Name
|
||||
// TypeArgs are the generic type arguments for the struct type.
|
||||
TypeArgs []*types.Type
|
||||
Fields []StructLiteralField
|
||||
}
|
||||
|
||||
type StructLiteralField struct {
|
||||
Name string
|
||||
Value any
|
||||
}
|
||||
|
||||
// ParamResult represents a parameter or a result of a function.
|
||||
type ParamResult struct {
|
||||
Name string
|
||||
|
||||
Reference in New Issue
Block a user