gencheck

package module
v0.1.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 17, 2020 License: Apache-2.0 Imports: 5 Imported by: 1

README

gencheck

CircleCI Coverage Status Go Report Card

Validation generation for go.
Built-in validations

See Validations

How it works

gencheck was built using the idea of zencoder/gokay, but uses templates to create validations for a struct.

gencheck will use the valid tag within a struct to generate a Validate() method, which is will store in a file_validators.go file next to the input file.

gencheck's Validate() method will return a ValidationErrors type, which is an array of FieldErrors.

Given the struct:

type MyStruct struct{
	MyField string `valid:"required"`
}

A Validate method is generated:

func (s MyStruct) Validate() error {
	var vErrors gencheck.ValidationErrors

	// BEGIN MyField Validations
	// required
	if s.MyField == "" {
		vErrors = append(vErrors, gencheck.NewFieldError("MyStruct", "MyField", "required", errors.New("is required")))
	}
	// END MyField Validations

	if len(vErrors) > 0 {
		return vErrors
	}
	return nil
}

Installing

First use go get to install the latest version of the library.

go get -v github.com/abice/gencheck/gencheck

Normally the above command will build and install the binary, but just to be sure it is installed in your GOPATH for use on the command line, or build args:

go install github.com/abice/gencheck/gencheck

Running

Command line
gencheck -f=file.go -t="SomeTemplate.tmpl" --template="SomeOtherTemplate.tmpl" -d="some/dir" --template-dir="some/dir/that/has/templates"
Using with go generate

Add a //go:generate tag to the top of your file that you want to generate for, including the file name.

//go:generate gencheck -f=this_file.go

Adding Validations

Add validations to valid tag in struct def:

type ExampleStruct struct {
	HexStringPtr            *string `valid:"len=16,notnil,hex"`
	HexString               string  `valid:"len=12,hex"`
	CanBeNilWithConstraints *string `valid:"len=12"`
}
Tag syntax

Validation tags are comma separated, with any validation parameter specified after an equal sign.

valid:"ValidationName1,ValidationName2=vn2param"

In the above example, the hex and notnil Validations are parameterless, whereas len requires 1 parameter.

Time comparisons

Since the addition of gt(e) and lt(e), there are now comparisons for time.Time values. If no arguments are specified to those, then it calculates whether the field time is After and Before time.Now().UTC() respectively. You can specify a parameter for those validations if you choose. The parameter will be interpreted as the offset to use with respect to time.Now().UTC() by utilizing the Add() function.

requestTime time.Time `valid:"gte=-1*time.Second"`
tGteTimeVal := time.Now().UTC().Add(-1 * time.Second)
if s.GteTimeVal.Before(tGteTimeVal) {
	vErrors = append(vErrors, gencheck.NewFieldError("Test", "GteTimeVal", "gte", fmt.Errorf("is before %s", tGteTimeVal)))
}
Fail Fast flag

The fail fast flag is a built-in validation flag that will allow you to return immediately on an invalid check. This allows you to not waste time checking the rest of the struct if a vital field is wrong. It can be placed anywhere within the valid tag, and will be applied to all rules within that field.

There is also a --failfast flag on the cli that will allow you to make all validations within all structs found in the files to be fail fast.

Writing your own Validations

gencheck allows developers to write and attach their own Validation templates to the generator.

  1. Write a template that creates a validation for a given field making sure to define the template as the validation tag you want to use:

    {{define "mycheck" -}}
    if err := gencheck.IsUUID({{.Param}}, {{if not (IsPtr . )}}&{{end}}s.{{.FieldName}}); err != nil {
      {{ AddError . "err" }}
    }
    {{end -}}
    
  2. Import that template when running gencheck

  3. Write tests for your struct's constraints

  4. Add valid tags to your struct fields

  5. Run gencheck: gencheck -f=file.go -t=MyTemplate

NOTES:

  • In your template, the . pipeline is an instance of the generator.Validation struct.
  • The template functions from Sprig have been included.
  • There are some custom functions provided for you to help in determining the ast field type
  • isPtr
  • addError
  • isNullable
  • isMap
  • isArray
  • isStruct
  • isStructPtr
  • isStructPtr
  • generationError
    • Allows you to fail code generation with a specific error message

More Examples

Useless Benchmarks

I know benchmarks are always skewed to show what the creators want you to see, but here's a quick benchmark of the cost of using validation to check.

I've also added some comparison benchmark output from the ./internal/benchmark_test.go to compare the different options with gencheck and how it holds up to the go playground validator.

BenchmarkReflectionInt-8      	20000000	       104 ns/op
BenchmarkEmptyInt-8           	2000000000	         0.29 ns/op
BenchmarkReflectionStruct-8   	 5000000	       262 ns/op
BenchmarkEmptyStruct-8        	50000000	        28.3 ns/op
BenchmarkReflectionString-8   	10000000	       159 ns/op
BenchmarkEmptyString-8        	200000000	         9.49 ns/op

Benchmarks using fail fast flag

BenchmarkValidString-8            	300000000	         5.02 ns/op
BenchmarkFailing1TestString-8     	10000000	       158 ns/op
BenchmarkFailing2TestString-8     	10000000	       159 ns/op
BenchmarkFailingAllTestString-8   	10000000	       164 ns/op

Benchmarks without fail fast flag and preallocated capacity for errors

BenchmarkValidString-8            	20000000	        68.7 ns/op
BenchmarkFailing1TestString-8     	10000000	       189 ns/op
BenchmarkFailing2TestString-8     	 5000000	       272 ns/op
BenchmarkFailingAllTestString-8   	 3000000	       418 ns/op

Development

Dependencies

Tested on go 1.7.3.

Build and run unit tests
make test
TODO
  • Testing for templates
  • Prevent duplicate validations on the same field
  • Update Required tag to error out on numerical or boolean fields
  • Support for sub-validations? Struct fields: generated code will call static Validate method on any field that implements Validateable interface Maybe use a deep check
  • Readme info for what information is available within the templates.
  • Contains for other slice types.
  • Contains for maps.
  • Add support for build tags for generated file.
  • Cross field validation (i.e. x.start <= x.end)
CI

This library builds on Circle CI, here.

License

Apache License Version 2.0

Documentation

Overview

Package gencheck is used for defining validation functions that are called in generated Validate functions.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsBCP47

func IsBCP47(s *string) error

IsBCP47 will return an error if the string is not able to be parsed as a language descriptor. http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers

func IsHex

func IsHex(s *string) error

IsHex validates that the given string is a hex value

func IsUUID

func IsUUID(s *string) error

IsUUID validates that the given string is a UUID value

func IsUUIDv3 added in v0.1.2

func IsUUIDv3(s *string) error

IsUUIDv3 validates that the given string is a UUIDv3 value

func IsUUIDv4 added in v0.1.2

func IsUUIDv4(s *string) error

IsUUIDv4 validates that the given string is a UUIDv4 value

func IsUUIDv5 added in v0.1.2

func IsUUIDv5(s *string) error

IsUUIDv5 validates that the given string is a UUIDv5 value

func Validate

func Validate(i interface{}) error

Validate calls validate on structs that implement the Validateable interface. If they do not, then that struct is valid.

Types

type FieldError

type FieldError interface {
	Tag() string
	Field() string
	Struct() string
	Message() string
	error
}

FieldError provide error information for a specific field validation.

func NewFieldError

func NewFieldError(st, field, tag string, err error) FieldError

NewFieldError returns a newly created immutable FieldError Tag is normally the rule that was invalid, but can be used for whatever message you want.

type Validateable

type Validateable interface {
	Validate() error
}

Validateable specifies a generic error return type instead of an ErrorMap return type in order to allow for handwritten Validate methods to work in tandem with gencheck generated Validate methods.

type ValidationErrors

type ValidationErrors []FieldError

ValidationErrors is an array of Field Errors

func (ValidationErrors) Error

func (ve ValidationErrors) Error() string

Error returns a string of the validation errors

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL