laravalidate

package module
v1.0.5 Latest Latest
Warning

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

Go to latest
Published: Nov 19, 2024 License: MIT Imports: 17 Imported by: 0

README

Laravalidate

A go package that has Laravel like validation.

It takes inspiration from Laravel's validation system but is not a 1 on 1 copy.

It's designed to make it easy to convert the validation rules from a Laravel project to a Go project.

go get -u github.com/mjarkk/laravalidate
package main

import (
	"context"
	"fmt"

	"github.com/mjarkk/laravalidate"
)

func main() {
	input := struct {
		Name string `json:"name" validate:"required"`
	}{
		Name: "",
	}

	err := laravalidate.JsonValidate(context.Background(), nil, input)
	fmt.Println(err.Error()) // The name field is required.
}

Rules

All the rules are defined in: RULES.md

Most laraval rules are supported*.

(*database related rules can be added using the dbrules package and currently only support sql databases)

Rules can be set using the validate tag on a struct field like:

type UserRequest struct {
	Email string `json:"email" validate:"email"`
	Name  string `json:"name" validate:"required"`
}

Multiple rules can be set by separating them with a | like:

type UserRequest struct {
	Email string `json:"email" validate:"required|email"`
}

When validating an array the array can be validated using the validate tag and it's elements can be validated using the validateInner tag like:

type UserRequest struct {
	// The emails list is required and must at least contain one element.
	// The elements in the list must be valid email addresses.
	Emails []string `json:"emails" validate:"required" validateInner:"email"`
}

Database rules

Database rules are not out of the box provided as they require a database connection.

For sql connections they can be implemented using the dbrules package.

func main() {
	db, err := sql.Open(driverName, dns)
	// Check err

	dbrules.AddRules(db, dbrules.DefaultStyle)

	// Now you can add translations!
	// translations.RegisterNlTranslations()
}

More error info

type UserRequest struct {
	Email string `json:"email" validate:"email"`
	Name  string `json:"name" validate:"required"`
}

func main() {
	err := laravalidate.JsonValidate(context.Background(), nil, UserRequest{})
	if err == nil {
		os.Exit(0)
	}

	errInfo := err.(*laravalidate.ValidationError)
	for _, fieldError := range errInfo.Errors {
		fmt.Println("field:", fieldError.JsonPath)
		for _, err := range fieldError.Errors {
			fmt.Printf("  %+v\n", err)
		}
	}
}

Laravel style errors

For when you are converting a laravel application to a go application and want to keep the error messages the same.

type UserRequest struct {
	Email string `json:"email" validate:"email"`
	Name  string `json:"name" validate:"required"`
}

func main() {
	err := laravalidate.JsonValidate(context.Background(), nil, UserRequest{})
	if err == nil {
		os.Exit(0)
	}

	laravelErr := err.(*laravalidate.ValidationError).ToLaravelError()
	laravelErrJson, err := json.MarshalIndent(laravelErr, "", "  ")
	if err != nil {
		panic(err)
	}

	fmt.Println(string(laravelErrJson))
	/*{
	  "errors": {
	    "email": [
	      "The email field must be a valid email address."
	    ],
	    "name": [
	      "The name field is required."
	    ]
	  },
	  "message": "Form contains errors"
  }*/
}

Go style errors

It might be that you are not validating json messages but rather go structs or something else. In these cases you might want the go names rather than the json names in the error messages.

This can be done using the laravalidate.GoValidate method

func main() {
	// If you are using the db rules,
	// you should add them before registering any translations!

	translations.RegisterNlTranslations()

	input := struct {
		Name string `json:"name" validate:"required"`
	}{
		Name: "",
	}

	err := laravalidate.GoValidate(context.Background(), nil, input)
	fmt.Println(err.Error()) // The Name field is required.
}

Custom error messages

Sometimes you want to provide a custom error message for a specific field.

type UserRequest struct {
	Email string `json:"email" validate:"email"`
	Name  string `json:"name" validate:"required"`
}

func (UserRequest) ValidationMessages() []laravalidate.CustomError {
	return []laravalidate.CustomError{
		{Key: "Email", Resolver: laravalidate.BasicMessageResolver("Bro email is required!")},
	}
}

func main() {
	err := laravalidate.JsonValidate(context.Background(), nil, UserRequest{})
	fmt.Println(err.Error()) // Bro email is required!
}

Nested and spesific validators fields can be accessed using the . separator. For example when a field is nested under Foo[3].Bar.Baz[2] you can define the keys for it the following ways:

  • Foo.3.Bar.Baz.2 (very strict)
  • Foo.3.Bar.Baz.2.required (only triggers on required rule)
  • Foo.*.Bar.Baz.* (lists have wild cards)
  • Foo.Bar.Baz (same as the prevouse one)
  • Foo.Bar.Baz.required (only triggers on the required rule)

There are also some variables that can be used in the custom error messages:

  • :attribute - The name of the field
  • :value - The value of the field
  • :other - If the value is compared to another value this will be the other value
  • :date - The date that is being validated in the DateTime format 2006-01-02 15:04:05
  • :args - All the argument provided to the validator
  • :arg0..x (arg4) - A specific argument provided to the validator by index (0 based)

Translations

You can provide a list of languages to the JsonValidate function to get translated errors. This only works if you have registered custom error messages for a specific language.

package main

import (
	"context"
	"fmt"

	"github.com/mjarkk/laravalidate"
	"github.com/mjarkk/laravalidate/translations"
	"golang.org/x/text/language"
)

func main() {
	// Note that we have to register the translations before we can use them.
	translations.RegisterNlTranslations()

	input := struct {
		Name string `json:"name" validate:"required"`
	}{
		Name: "",
	}

	err := laravalidate.JsonValidate(
		context.Background(),
		[]language.Tag{
			language.Dutch,
		},
		input,
	)
	fmt.Println(err.Error()) // Het name veld is verplicht.
}

Out of the box supported languages are:

  • English (default)
  • German translations.RegisterDeTranslations()
  • Dutch translations.RegisterNlTranslations()
  • French translations.RegisterFrTranslations()
  • Spanish translations.RegisterEsTranslations()

Custom translations

See how other translations are done inside of the ./translations folder

Writing a custom validator

// Accepted is a custom validator that checks if the value is accepted.
//
// It returns 2 values:
// - string : Hint for the error message, this can be used to change error messages based on the hint.
// - bool : Passes, if false the validation failed and you'll get an error message
//
// Note that this validator is already part of the laravel validation rules and just here for example.
func Accepted(ctx *ValidatorCtx) (string, bool) {
	// Unwrap the pointer to the actual value if it is a pointer.
	// Note that this might result in a type but no value inside the ctx.
	ctx.UnwrapPointer()

	// Check if the value type is one of the following:
	if !ctx.IsKind(
		reflect.Bool,
		reflect.String,
		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
	) {
		return "invalid_type", false
	}

	// Check if the ctx has a value
	if !ctx.HasValue() {
		return "unacceptable", false
	}

	switch ctx.Kind() {
	case reflect.Bool:
		if !ctx.Value.Bool() {
			return "unacceptable", false
		}
	case reflect.String:
		switch ctx.Value.String() {
		case "yes", "on", "1", "true":
			return "", true
		}
		return "unacceptable", false
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		if ctx.Value.Int() == 1 {
			return "", true
		}
		return "unacceptable", false
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		if ctx.Value.Uint() == 1 {
			return "", true
		}
		return "unacceptable", false
	default:
		return "invalid_type", false
	}

	return "", true
}

func main() {
	laravalidate.RegisterValidator("accepted", Accepted)
	laravalidate.BaseRegisterMessages(map[string]laravalidate.MessageResolver{
		"accepted": laravalidate.BasicMessageResolver("The :attribute field must be accepted."),
	})
	// Now you can use the accepted validator
}

There are a lot more methods on the ValidatorCtx that you can use to get the value of the field.

See the rules.go for examples.

Documentation

Index

Constants

View Source
const (
	ParsedDateKey = "parsed-date" // Stores a custom parsed date, this is used by the date_format validator to store the results so they can be used by other validators
)

Variables

View Source
var FallbackMessageResolver = BasicMessageResolver("The :attribute field is invalid")

Functions

func Accepted

func Accepted(ctx *ValidatorCtx) (string, bool)

func ActiveUrl

func ActiveUrl(ctx *ValidatorCtx) (string, bool)

func AfterDate

func AfterDate(ctx *ValidatorCtx) (string, bool)

func AfterOrEqualDate

func AfterOrEqualDate(ctx *ValidatorCtx) (string, bool)

func Alpha

func Alpha(ctx *ValidatorCtx) (string, bool)

func AlphaDash

func AlphaDash(ctx *ValidatorCtx) (string, bool)

func AlphaNumeric

func AlphaNumeric(ctx *ValidatorCtx) (string, bool)

func Ascii

func Ascii(ctx *ValidatorCtx) (string, bool)

func Bail

func Bail(ctx *ValidatorCtx) (string, bool)

func BaseRegisterMessages

func BaseRegisterMessages(resolvers map[string]MessageResolver)

func BeforeDate

func BeforeDate(ctx *ValidatorCtx) (string, bool)

func BeforeOrEqualDate

func BeforeOrEqualDate(ctx *ValidatorCtx) (string, bool)

func Between

func Between(ctx *ValidatorCtx) (string, bool)

func Boolean

func Boolean(ctx *ValidatorCtx) (string, bool)

func Confirmed

func Confirmed(ctx *ValidatorCtx) (string, bool)

func CreateGoError added in v1.0.3

func CreateGoError(errors map[string]string) error

func CreateJsonError added in v1.0.3

func CreateJsonError(errors map[string]string) error

func Date

func Date(ctx *ValidatorCtx) (string, bool)

func DateFormat

func DateFormat(ctx *ValidatorCtx) (string, bool)

func Declined

func Declined(ctx *ValidatorCtx) (string, bool)

func Digits

func Digits(ctx *ValidatorCtx) (string, bool)

func DigitsBetween

func DigitsBetween(ctx *ValidatorCtx) (string, bool)

func Email

func Email(ctx *ValidatorCtx) (string, bool)

func EndsWith

func EndsWith(ctx *ValidatorCtx) (string, bool)

func Extensions

func Extensions(ctx *ValidatorCtx) (string, bool)

func Filled

func Filled(ctx *ValidatorCtx) (string, bool)

func GoValidate

func GoValidate(ctx context.Context, languages []language.Tag, input any) error

GoValidate should be used to validate something within a go codebase with validation errors that apply to the go codebase.

If an error is returned the type should be of *ValidationError.

Ctx can be set to nil, default value will be context.Background(). Languages can be set to nil, default value will be []language.Tag{language.English}.

func Gt added in v1.0.1

func Gt(ctx *ValidatorCtx) (string, bool)

func Gte added in v1.0.1

func Gte(ctx *ValidatorCtx) (string, bool)

func HexColor

func HexColor(ctx *ValidatorCtx) (string, bool)

func IP

func IP(ctx *ValidatorCtx) (string, bool)

func IPV4

func IPV4(ctx *ValidatorCtx) (string, bool)

func IPV6

func IPV6(ctx *ValidatorCtx) (string, bool)

func In

func In(ctx *ValidatorCtx) (string, bool)

func JSON

func JSON(ctx *ValidatorCtx) (string, bool)

func JsonValidate

func JsonValidate(ctx context.Context, languages []language.Tag, input any) error

JsonValidate should be used to validate a json parsed message, errors returned will have a json paths.

If an error is returned the type should be of *ValidationError.

Ctx can be set to nil, default value will be context.Background(). Languages can be set to nil, default value will be []language.Tag{language.English}.

func LogValidatorsWithoutMessages

func LogValidatorsWithoutMessages()

func Lowercase

func Lowercase(ctx *ValidatorCtx) (string, bool)

func Lt added in v1.0.1

func Lt(ctx *ValidatorCtx) (string, bool)

func Lte added in v1.0.1

func Lte(ctx *ValidatorCtx) (string, bool)

func MacAddress

func MacAddress(ctx *ValidatorCtx) (string, bool)

func Max

func Max(ctx *ValidatorCtx) (string, bool)

func MaxDigits

func MaxDigits(ctx *ValidatorCtx) (string, bool)

func Mimes

func Mimes(ctx *ValidatorCtx) (string, bool)

func Mimetypes

func Mimetypes(ctx *ValidatorCtx) (string, bool)

func Min

func Min(ctx *ValidatorCtx) (string, bool)

func MinDigits

func MinDigits(ctx *ValidatorCtx) (string, bool)

func NotIn

func NotIn(ctx *ValidatorCtx) (string, bool)

func NotNil

func NotNil(ctx *ValidatorCtx) (string, bool)

func NotRegex

func NotRegex(ctx *ValidatorCtx) (string, bool)

func Numeric

func Numeric(ctx *ValidatorCtx) (string, bool)

func Regex

func Regex(ctx *ValidatorCtx) (string, bool)

func RegisterMessages

func RegisterMessages(lang language.Tag, resolvers map[string]MessageResolver)

RegisterMessages registers messages for a validator

func RegisterMessagesStrict

func RegisterMessagesStrict(lang language.Tag, resolvers map[string]MessageResolver)

RegisterMessagesStrict registers messages for a validator Compared to RegisterMessages this function does not try to match the language with the base language For example if you register a message for "en-GB" it will only be used for "en-GB" and not for "en"

func RegisterValidator

func RegisterValidator(name string, validator ValidatorFn)

RegisterValidator registers a new validator function

func Required

func Required(ctx *ValidatorCtx) (string, bool)

func Size

func Size(ctx *ValidatorCtx) (string, bool)

func StartsWith

func StartsWith(ctx *ValidatorCtx) (string, bool)

func URL

func URL(ctx *ValidatorCtx) (string, bool)

func Ulid

func Ulid(ctx *ValidatorCtx) (string, bool)

func Uppercase

func Uppercase(ctx *ValidatorCtx) (string, bool)

func Uuid

func Uuid(ctx *ValidatorCtx) (string, bool)

Types

type BasicMessageResolver

type BasicMessageResolver string

BasicMessageResolver is a simple message resolver that always returns the same message, no matter the language or hint

func (BasicMessageResolver) Resolve

func (d BasicMessageResolver) Resolve(hint string) string

type ConvertStatus

type ConvertStatus uint8
const (
	ConverstionOk ConvertStatus = iota
	InvalidType                 // Usually means you should return ("invalid_type", false)
	Invalid                     // Usually means you should return ("invalid", false)
	ValueNil                    // Usually means you should return ("", true)
)

func (ConvertStatus) Oke

func (s ConvertStatus) Oke() bool

func (ConvertStatus) Response

func (s ConvertStatus) Response() (string, bool)

type CustomError

type CustomError struct {
	Key      string
	Resolver MessageResolver
}

type DigitsStatus

type DigitsStatus uint8
const (
	DigitStatusValid DigitsStatus = iota
	DigitStatusInvalidType
	DigitStatusNil
	DigitStatusStringWithoutDigits
)

func (DigitsStatus) Oke

func (s DigitsStatus) Oke() bool

func (DigitsStatus) Response

func (s DigitsStatus) Response() (string, bool)

type FieldErrors

type FieldErrors struct {
	Path   string                `json:"path"`
	Errors []FieldValidatorError `json:"errors"`
}

type FieldValidatorError

type FieldValidatorError struct {
	Rule    string `json:"rule"`
	Hint    string `json:"hint"`
	Message string `json:"message"`
}

type LaravelErrorObj

type LaravelErrorObj []LaravelErrorObjEntry

func (LaravelErrorObj) MarshalJSON

func (obj LaravelErrorObj) MarshalJSON() ([]byte, error)

type LaravelErrorObjEntry

type LaravelErrorObjEntry struct {
	Key   string
	Value []string
}

type LaravelValidationError

type LaravelValidationError struct {
	Errors  LaravelErrorObj `json:"errors"`
	Message string          `json:"message"`
}

func (*LaravelValidationError) Error added in v1.0.4

func (v *LaravelValidationError) Error() string

type MessageHintResolver

type MessageHintResolver struct {
	Fallback string
	Hints    map[string]string
}

func (MessageHintResolver) Resolve

func (d MessageHintResolver) Resolve(hint string) string

type MessageResolver

type MessageResolver interface {
	Resolve(hint string) string
}

type Mode

type Mode uint8
const (
	GoMode Mode = iota
	JsonMode
)

type Needle

type Needle struct {
	Type reflect.Type
	// Value might be nil if we are validating a pointer to a nil struct.
	// Also running ctx.UnwrapPointer() might cause the value to be unset
	Value *reflect.Value
}

func (*Needle) Date

func (n *Needle) Date() (time.Time, ConvertStatus)

Date tries to convert the value to a time.Time

func (*Needle) Float64 added in v1.0.1

func (n *Needle) Float64() (float64, bool)

Float64 tries to convert the number to a float64

func (*Needle) HasLen

func (n *Needle) HasLen() bool

HasLen returns true if the type kind supports the reflect.Len method

func (*Needle) HasValue

func (n *Needle) HasValue() bool

HasValue returns if the needle has a value

func (*Needle) Int64 added in v1.0.1

func (n *Needle) Int64() (int64, bool)

Int64 tries to convert the number to a int64

func (*Needle) IsFloat

func (n *Needle) IsFloat() bool

IsFloat returns true if the type kind is a float

func (*Needle) IsInt

func (n *Needle) IsInt() bool

IsInt returns true if the type kind is a int[..]

func (*Needle) IsKind

func (n *Needle) IsKind(kinds ...reflect.Kind) bool

IsKinds asserts the .Kind method for one of the input values If it matches one it returns true

func (*Needle) IsList added in v1.0.1

func (n *Needle) IsList() bool

IsBool returns true if the type kind is a list

func (*Needle) IsNumeric

func (n *Needle) IsNumeric() bool

IsNumeric returns true if the type kind is a int[..] , uint[..] or float[..]

func (*Needle) IsUint

func (n *Needle) IsUint() bool

IsUint returns true if the type kind is a uint[..]

func (*Needle) Kind

func (n *Needle) Kind() reflect.Kind

Kind returns the kind of the value if available an falls back to the kind of the type

func (*Needle) String

func (n *Needle) String() (string, ConvertStatus)

String is a wrapper around (*ValidatorCtx).value.String() This one checks the following extra things and if one of them does not match returns ("", false) 1. The value is set 2. The value is a string

Example: ```

str, status := ctx.String()

if !status.Oke() {
  return status.Response()
}

fmt.Println(str)

```

func (*Needle) StringLike

func (n *Needle) StringLike() (string, ConvertStatus)

StringLike returns all values that can be interpreted as a string

Supported: - string - []byte - []rune - rune - byte

Example: ```

str, status := ctx.StringLike()

if !status.Oke() {
  return status.Response()
}

fmt.Println(str)

```

func (*Needle) UnwrapPointer

func (n *Needle) UnwrapPointer() bool

UnwrapPointer unwraps the pointer

type SimpleStackElement

type SimpleStackElement struct {
	GoName   string
	JsonName string
	Index    int // Only for kind == StackKindList
	Kind     StackKind
}

type SizeCompareStatus added in v1.0.1

type SizeCompareStatus uint8
const (
	SizeCompareStatusEq SizeCompareStatus = iota
	SizeCompareStatusLt
	SizeCompareStatusGt
)

type Stack

type Stack []StackElement

func (Stack) AppendField

func (s Stack) AppendField(field reflect.StructField, parent *reflect.Value, parentType reflect.Type) Stack

func (Stack) AppendIndex

func (s Stack) AppendIndex(index int, parent *reflect.Value, parentType reflect.Type) Stack

func (Stack) LooslyEqualsWithRule

func (s Stack) LooslyEqualsWithRule(key string, rule string) bool

LooslyEquals checks if the stack is equal to the given key The key might ignore the index of the list elements and only check the object fields

func (Stack) ToPaths

func (s Stack) ToPaths() (golang, json string)

type StackElement

type StackElement struct {
	GoName     string
	JsonName   string
	Index      int // Only for kind == StackKindList
	Kind       StackKind
	Parent     *reflect.Value
	ParentType reflect.Type
}

type StackKind

type StackKind uint8
const (
	StackKindObject StackKind = iota
	StackKindList
)

type ValidationError

type ValidationError struct {
	Mode     Mode          `json:"mode"`
	Language []string      `json:"language"`
	Errors   []FieldErrors `json:"errors"`
}

func (*ValidationError) Error

func (v *ValidationError) Error() string

func (*ValidationError) Prefix added in v1.0.4

func (e *ValidationError) Prefix(prefix string)

Prefix prefixes all paths in the error with the given prefix

func (*ValidationError) ToLaravelError

func (e *ValidationError) ToLaravelError() *LaravelValidationError

ToLaravelError converts a ValidationError to Laravel like validation error

type Validator

type Validator struct {
	// contains filtered or unexported fields
}

func (*Validator) CustomValidationRule

func (v *Validator) CustomValidationRule(ruleName string, stack Stack) MessageResolver

func (*Validator) CustomValidationRules

func (v *Validator) CustomValidationRules() []CustomError

func (*Validator) Error

func (v *Validator) Error() error

func (*Validator) ErrorMessage

func (v *Validator) ErrorMessage(ruleName string, resolvers map[string]MessageResolver, hint string, ctx *ValidatorCtx) string

func (*Validator) ErrorMessageTemplate

func (v *Validator) ErrorMessageTemplate(ruleName string, resolvers map[string]MessageResolver, hint string, stack Stack) string

func (*Validator) List

func (v *Validator) List(stack Stack, value reflect.Value, validateInner []validationRule)

func (*Validator) Nil

func (v *Validator) Nil(stack Stack, valueType reflect.Type)

func (*Validator) NilStruct

func (v *Validator) NilStruct(stack Stack, valueType reflect.Type)

func (*Validator) Struct

func (v *Validator) Struct(stack Stack, value reflect.Value)

func (*Validator) Validate

func (v *Validator) Validate(stack Stack, value *reflect.Value, valueType reflect.Type, rules []validationRule)

type ValidatorCtx

type ValidatorCtx struct {
	// Needle contains the field under validation
	Needle
	// Args are the arguments that were passed to the validator
	Args []string
	// contains filtered or unexported fields
}

func (*ValidatorCtx) Bail

func (ctx *ValidatorCtx) Bail()

Bail indicates that the validator should stop after the first error for this field

func (*ValidatorCtx) BailStatus

func (ctx *ValidatorCtx) BailStatus() bool

BailStatus returns the current bail status, if true the validator will stop after the first error

func (*ValidatorCtx) Context

func (ctx *ValidatorCtx) Context() context.Context

NewValidatorCtx returns the underlying context of the validator

func (*ValidatorCtx) Date

func (ctx *ValidatorCtx) Date() (time.Time, ConvertStatus)

Date tries to convert the value to a time.Time

func (*ValidatorCtx) DateFromArgs

func (ctx *ValidatorCtx) DateFromArgs(argIndex int) (time.Time, bool)

func (*ValidatorCtx) Field

func (ctx *ValidatorCtx) Field(key string) *Needle

field tries to return a value from the input based on the requested path There are 2 main ways of using this function

1. Absolute path:

  • "foo.1.bar" = Get from the input (struct) the field "foo", then when it's a list like get the element at index 1 from the list, then get the field "bar" from the struct
  • "" = Get the source input

2. Relative path:

  • ".foo" = Get relative to the currently processed struct the field "foo"
  • ".1" = Get relative to the currently processed list the element at index 1
  • "." = Get the currently processed struct
  • "..foo" = Get the parent of the currentl`y processed struct and then get the field "foo" from it

If nil is returned the field does not exist or path is invalid If a needle with only a reflect.Type is returned the path exists but the value is nil

func (*ValidatorCtx) GetState

func (ctx *ValidatorCtx) GetState(key string) (any, bool)

GetState gets a value from the state

func (*ValidatorCtx) ObjectFieldName

func (ctx *ValidatorCtx) ObjectFieldName() string

ObjectFieldName returns the name of the currently processed field in the object If the currently processed field is not an object field it will return an empty string

func (*ValidatorCtx) SetState

func (ctx *ValidatorCtx) SetState(key string, value any)

SetState sets a value in the state

func (*ValidatorCtx) Stack

func (ctx *ValidatorCtx) Stack() Stack

Stack returns the path to the currently processed field !!DO NOT MODIFY THE STACK!!, it will break the validator and cause panics

func (*ValidatorCtx) UnBail

func (ctx *ValidatorCtx) UnBail()

UnBail indicates that the validator should continue after the first error for this field

type ValidatorCtxState

type ValidatorCtxState struct {
	// contains filtered or unexported fields
}

type ValidatorFn

type ValidatorFn func(ctx *ValidatorCtx) (string, bool)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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