github.com/aavshr/aws-sdk-go@v1.41.3/private/model/api/shape_validation.go (about)

     1  //go:build codegen
     2  // +build codegen
     3  
     4  package api
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"text/template"
    10  )
    11  
    12  // A ShapeValidationType is the type of validation that a shape needs
    13  type ShapeValidationType int
    14  
    15  const (
    16  	// ShapeValidationRequired states the shape must be set
    17  	ShapeValidationRequired = iota
    18  
    19  	// ShapeValidationMinVal states the shape must have at least a number of
    20  	// elements, or for numbers a minimum value
    21  	ShapeValidationMinVal
    22  
    23  	// ShapeValidationNested states the shape has nested values that need
    24  	// to be validated
    25  	ShapeValidationNested
    26  )
    27  
    28  // A ShapeValidation contains information about a shape and the type of validation
    29  // that is needed
    30  type ShapeValidation struct {
    31  	// Name of the shape to be validated
    32  	Name string
    33  	// Reference to the shape within the context the shape is referenced
    34  	Ref *ShapeRef
    35  	// Type of validation needed
    36  	Type ShapeValidationType
    37  }
    38  
    39  var validationGoCodeTmpls = template.Must(
    40  	template.New("validationGoCodeTmpls").
    41  		Funcs(template.FuncMap{
    42  			"getMin": func(ref *ShapeRef) float64 {
    43  				if !ref.CanBeEmpty() && ref.Shape.Min <= 0 {
    44  					return 1
    45  				}
    46  
    47  				return ref.Shape.Min
    48  			},
    49  		}).
    50  		Parse(`
    51  {{ define "requiredValue" -}}
    52      if s.{{ .Name }} == nil {
    53  		invalidParams.Add(request.NewErrParamRequired("{{ .Name }}"))
    54      }
    55  {{- end }}
    56  {{ define "minLen" -}}
    57  	{{- $min := getMin .Ref -}}
    58  	if s.{{ .Name }} != nil && len(s.{{ .Name }}) < {{ $min }} {
    59  		invalidParams.Add(request.NewErrParamMinLen("{{ .Name }}", {{ $min }}))
    60  	}
    61  {{- end }}
    62  {{ define "minLenString" -}}
    63  	{{- $min := getMin .Ref -}}
    64  	if s.{{ .Name }} != nil && len(*s.{{ .Name }}) < {{ $min }} {
    65  		invalidParams.Add(request.NewErrParamMinLen("{{ .Name }}", {{ $min }}))
    66  	}
    67  {{- end }}
    68  {{ define "minVal" -}}
    69  	{{- $min := getMin .Ref -}}
    70  	if s.{{ .Name }} != nil && *s.{{ .Name }} < {{ $min }} {
    71  		invalidParams.Add(request.NewErrParamMinValue("{{ .Name }}", {{ $min }}))
    72  	}
    73  {{- end }}
    74  {{ define "nestedMapList" -}}
    75      if s.{{ .Name }} != nil {
    76  		for i, v := range s.{{ .Name }} {
    77  			if v == nil { continue }
    78  			if err := v.Validate(); err != nil {
    79  				invalidParams.AddNested(fmt.Sprintf("%s[%v]", "{{ .Name }}", i), err.(request.ErrInvalidParams))
    80  			}
    81  		}
    82  	}
    83  {{- end }}
    84  {{ define "nestedStruct" -}}
    85      if s.{{ .Name }} != nil {
    86  		if err := s.{{ .Name }}.Validate(); err != nil {
    87  			invalidParams.AddNested("{{ .Name }}", err.(request.ErrInvalidParams))
    88  		}
    89  	}
    90  {{- end }}
    91  `))
    92  
    93  // GoCode returns the generated Go code for the Shape with its validation type.
    94  func (sv ShapeValidation) GoCode() string {
    95  	var err error
    96  
    97  	w := &bytes.Buffer{}
    98  	switch sv.Type {
    99  	case ShapeValidationRequired:
   100  		err = validationGoCodeTmpls.ExecuteTemplate(w, "requiredValue", sv)
   101  	case ShapeValidationMinVal:
   102  		switch sv.Ref.Shape.Type {
   103  		case "list", "map", "blob":
   104  			err = validationGoCodeTmpls.ExecuteTemplate(w, "minLen", sv)
   105  		case "string":
   106  			err = validationGoCodeTmpls.ExecuteTemplate(w, "minLenString", sv)
   107  		case "integer", "long", "float", "double":
   108  			err = validationGoCodeTmpls.ExecuteTemplate(w, "minVal", sv)
   109  		default:
   110  			panic(fmt.Sprintf("ShapeValidation.GoCode, %s's type %s, no min value handling",
   111  				sv.Name, sv.Ref.Shape.Type))
   112  		}
   113  	case ShapeValidationNested:
   114  		switch sv.Ref.Shape.Type {
   115  		case "map", "list":
   116  			err = validationGoCodeTmpls.ExecuteTemplate(w, "nestedMapList", sv)
   117  		default:
   118  			err = validationGoCodeTmpls.ExecuteTemplate(w, "nestedStruct", sv)
   119  		}
   120  	default:
   121  		panic(fmt.Sprintf("ShapeValidation.GoCode, %s's type %d, unknown validation type",
   122  			sv.Name, sv.Type))
   123  	}
   124  
   125  	if err != nil {
   126  		panic(fmt.Sprintf("ShapeValidation.GoCode failed, err: %v", err))
   127  	}
   128  
   129  	return w.String()
   130  }
   131  
   132  // A ShapeValidations is a collection of shape validations needed nested within
   133  // a parent shape
   134  type ShapeValidations []ShapeValidation
   135  
   136  var validateShapeTmpl = template.Must(template.New("ValidateShape").Parse(`
   137  // Validate inspects the fields of the type to determine if they are valid.
   138  func (s *{{ .Shape.ShapeName }}) Validate() error {
   139  	invalidParams := request.ErrInvalidParams{Context: "{{ .Shape.ShapeName }}"}
   140  	{{ range $_, $v := .Validations -}}
   141  		{{ $v.GoCode }}
   142  	{{ end }}
   143  	if invalidParams.Len() > 0 {
   144  		return invalidParams
   145  	}
   146  	return nil
   147  }
   148  `))
   149  
   150  // GoCode generates the Go code needed to perform validations for the
   151  // shape and its nested fields.
   152  func (vs ShapeValidations) GoCode(shape *Shape) string {
   153  	buf := &bytes.Buffer{}
   154  	validateShapeTmpl.Execute(buf, map[string]interface{}{
   155  		"Shape":       shape,
   156  		"Validations": vs,
   157  	})
   158  	return buf.String()
   159  }
   160  
   161  // Has returns true or false if the ShapeValidations already contains the
   162  // the reference and validation type.
   163  func (vs ShapeValidations) Has(ref *ShapeRef, typ ShapeValidationType) bool {
   164  	for _, v := range vs {
   165  		if v.Ref == ref && v.Type == typ {
   166  			return true
   167  		}
   168  	}
   169  	return false
   170  }