validation

package
v0.29.0 Latest Latest
Warning

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

Go to latest
Published: Sep 7, 2026 License: MIT Imports: 28 Imported by: 0

Documentation

Overview

Package validation checks a submitted request against a set of rules.

A rule is written as a string, and a field's rules are one string:

var Register = validation.MustCompile(validation.Rules{
	"name":     "required|max:255",
	"email":    "required|email",
	"password": "required|min:12|confirmed",
})

in, err := ctx.Validate(requests.Register)
if err != nil {
	return err
}

A rule set is compiled ONCE, in a package-level variable, and every rule string is parsed and checked there: an unknown rule, a missing or unparseable argument, a pattern that does not compile, a cross-field reference naming a field that does not exist -- each of those fails at boot, naming the field, the rule and the file, and all of them are reported together. A rule set that boots is a rule set whose names are all real.

Where the files are

attributes.go    one ValidateX method per rule
rulebuilders.go  the typed builders, for a rule nobody wants to spell
validator.go     the Validator, which runs a compiled set over one request
messages.go      Errors, the messages a failed run collected
compile.go       the boot-time compile; parser.go is the per-request half
rules.go         the catalogue: arity, check and message of every rule

Four things worth knowing before writing a rule string

  • date_format takes a GO layout -- date_format:2006-01-02, never date_format:Y-m-d. It is a layout in the host language, and it is checked at boot.
  • regex takes a GO pattern: RE2, without delimiters, without backreferences and without lookaround. It is compiled at boot.
  • `unique` and `exists` take an auth Grant and a PresenceVerifier, given with WithPresence, and fail closed without one. A read is authorized like any other: "the validator only counts rows" is how a count of rows becomes a way to ask whether another tenant has a user with a given address.
  • `current_password` takes a CurrentPasswordChecker. Nothing here resolves a guard or a hasher, so without a checker the rule fails closed.

Reading what passed

There is no reflection and there are no struct tags. The rule set is data, the request struct is written by hand or generated, and the values that passed are read out of Input one at a time -- so a field nobody declared a rule for cannot reach a repository through here.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Confirmed

func Confirmed(e Errors, field, value, confirmation string)

Confirmed rejects a value its confirmation field does not repeat.

It is what a "confirm your password" box is for, and the message goes on the confirmation rather than on the field itself: a form that reports "password does not match" next to the first box tells the person to change the one they meant, and they change it, and the form fails again.

An empty confirmation is reported here rather than by Required, so the field gets one message instead of two saying the same thing.

func Email

func Email(e Errors, field, value string)

Email checks the shape only. Deliverability is proven by sending mail, never by a regular expression.

Whitespace is rejected rather than trimmed: an address with a space in it is almost always a paste accident, and silently trimming input hides the mistake from the person who made it.

The shape itself is emailShape, which the `email` rule also calls: two implementations of "is this an address" would drift, and the one that drifted would be whichever a screen did not exercise.

func Extend

func Extend(rule string, extension ExtensionFunc, message string)

Extend registers a rule of the application's own: a rule name, the check behind it, and the sentence it says.

It is called from an init or from main, before any rule set is compiled. An empty message leaves the rule saying "is not valid".

func ExtendDependent

func ExtendDependent(rule string, extension ExtensionFunc, message string)

ExtendDependent is Extend for a rule whose first parameter names another field.

func ExtendImplicit

func ExtendImplicit(rule string, extension ExtensionFunc, message string)

ExtendImplicit is Extend for a rule that runs even when the value is blank.

func GetLeadingExplicitAttributePath

func GetLeadingExplicitAttributePath(attribute string) string

GetLeadingExplicitAttributePath returns the part of a path before its first wildcard: "foo.bar.*.baz" gives "foo.bar", which is all of it that can be walked without guessing.

func InitializeAndGatherData

func InitializeAndGatherData(attribute string, masterData Data) map[string]any

InitializeAndGatherData returns the slice of the request one attribute names, flattened, plus the exact keys a wildcard matched.

func MaxLen

func MaxLen(e Errors, field, value string, n int)

MaxLen counts runes, not bytes.

func MinLen

func MinLen(e Errors, field, value string, n int)

MinLen counts runes, not bytes: a limit measured in bytes rejects valid input in any language that needs more than one byte per character.

func NotZero

func NotZero[T comparable](e Errors, field string, value T)

NotZero reports a value that was never filled in.

It is Required for everything that is not text. Required takes a string and the generator used to hand it a literal "" for an int, a date or an amount -- so every required field of those types failed validation with "is required" no matter what was sent, and the generated create endpoint could not be used at all. Found by audit.

A time.Time is asked rather than compared: a parsed "0001-01-01T00:00:00Z" carries a location the zero value does not, so == says they differ when they do not.

Bool has no meaningful zero to reject -- false is an answer, not an absence -- and the specification refuses `required` on a bool for that reason.

func Parse

func Parse(rule string) (string, []string)

Parse splits a rule string into its name and the parameters written after the colon.

The name stays as it was typed, which is how every table in this package keys it. Its two aliases are normalized: "int" is "integer" and "bool" is "boolean".

A malformed parameter list answers with no parameters rather than an error; Compile is where a malformed list is reported, with the field and the file.

func PasswordDefaults

func PasswordDefaults(callback func() *Password)

PasswordDefaults registers the policy every later PasswordDefault answers with.

func Required

func Required(e Errors, field, value string)

Required rejects an empty or blank value. It is the `required` rule for one value, without a rule set.

func ToKilobytes

func ToKilobytes(size string) (int, bool)

ToKilobytes reads a size written with a suffix: "2mb" is 2000 kilobytes -- a thousand, not 1024.

The bool is false for a suffix it does not know, and for a number written with no suffix at all.

Types

type AnyOf

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

AnyOf is the rule that the value passes when it passes any one of the given rule sets. Build one with NewAnyOf.

func NewAnyOf

func NewAnyOf(sets ...*Set) *AnyOf

NewAnyOf returns an AnyOf over the given sets.

func (*AnyOf) Message

func (r *AnyOf) Message() []string

Message returns the sentence for a value that passed none of the sets.

func (*AnyOf) Passes

func (r *AnyOf) Passes(attribute string, value any) bool

Passes reports whether the value passes any one of the sets, each run over the validator's data with this attribute replaced.

func (*AnyOf) SetValidator

func (r *AnyOf) SetValidator(validator *Validator)

SetValidator hands the rule the validator running it, which is where the data and the translator come from.

type ArrayRule

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

ArrayRule builds the "array" rule: the value must be an array, and when keys are given it may hold no key outside that list, which is how a nested object arriving from a form is kept to the shape it was meant to have. With no keys it renders the bare "array". The name carries a suffix because array is a type.

func NewArrayRule

func NewArrayRule(keys ...string) *ArrayRule

NewArrayRule returns an `array` rule, restricted to the given keys when any are named.

func (*ArrayRule) String

func (r *ArrayRule) String() string

String renders the rule, with the keys when there are any.

type Can

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

Can is the rule that the value is one the current subject is allowed to choose.

The question is asked through the callback the caller gives, against the Grant the validator carries. Build one with NewCan.

func NewCan

func NewCan(allows func(g auth.Grant, ability string, arguments []string, value any) bool, ability string, arguments ...string) *Can

NewCan returns a Can that asks allows about the ability.

func (*Can) Message

func (r *Can) Message() []string

Message returns the sentence for a value the subject may not choose.

func (*Can) Passes

func (r *Can) Passes(attribute string, value any) bool

Passes reports what the callback says about the Grant, the ability and the value.

A missing callback FAILS rather than passes: "nobody wired the authorizer" is not "everybody is allowed".

func (*Can) SetValidator

func (r *Can) SetValidator(validator *Validator)

SetValidator hands the rule the validator running it, which is where the Grant and the translator come from.

type ClosureValidationRule

type ClosureValidationRule struct {
	// Callback is the check itself.
	Callback func(attribute string, value any, fail func(message string), validator *Validator)

	// Failed reports whether the last Passes called fail at all.
	Failed bool
	// contains filtered or unexported fields
}

ClosureValidationRule is a rule written as a function rather than as a type, for a check that does not deserve a type of its own. The callback is handed the attribute, its value, a fail function and the validator, and reports a problem by calling fail with the sentence to show. Each call to fail adds one message, and a callback that never calls it passes.

func NewClosureValidationRule

func NewClosureValidationRule(callback func(attribute string, value any, fail func(message string), validator *Validator)) *ClosureValidationRule

NewClosureValidationRule returns a rule that runs the callback.

func (*ClosureValidationRule) Message

func (r *ClosureValidationRule) Message() []string

Message returns what the last Passes collected.

func (*ClosureValidationRule) Passes

func (r *ClosureValidationRule) Passes(attribute string, value any) bool

Passes runs the callback, and reports whether it called fail. A nil callback passes.

func (*ClosureValidationRule) SetValidator

func (r *ClosureValidationRule) SetValidator(validator *Validator)

SetValidator hands the rule the validator running it, which the callback is given as its fourth argument.

type CompilableRules

type CompilableRules interface {
	// Compile returns the rules this attribute gets, given what it holds.
	Compile(attribute string, value any, data Data) *ExplodedRules
}

CompilableRules is rules decided by looking at the value, which is what NestedRules is.

type CompileError

type CompileError struct {
	// Field is the input the rule was written against.
	Field string
	// Rule is the rule name, or empty when the failure is about the field as a
	// whole.
	Rule string
	// File and Line are where the rule set was compiled, from runtime.Caller --
	// so the message names the application's source, not this package's.
	File string
	Line int
	// Msg says what is wrong and, where there is one, where the answer lives.
	Msg string
}

CompileError is one thing wrong with a rule set, named precisely enough to fix without opening the framework.

func (CompileError) Error

func (e CompileError) Error() string

Error renders one failure on its own, with the file and line, because a single failure has to stand alone when it is the only one.

type CompileErrors

type CompileErrors []CompileError

CompileErrors is everything wrong with a rule set, not the first thing.

A set with three typos reports three. Reporting one at a time turns a boot check into three restarts, which is how a boot check gets a reputation for being slower than finding out on the request.

func (CompileErrors) Error

func (e CompileErrors) Error() string

Error renders the header once and one failure per line, the way kyse.Errors does, so a panic reads as a list rather than as a paragraph.

type ConditionalRules

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

ConditionalRules is the rules an attribute gets when a condition holds, and the ones it gets when it does not.

func NewConditionalRules

func NewConditionalRules(condition func(Data) bool, rules string, defaultRules ...string) *ConditionalRules

NewConditionalRules returns rules chosen by the condition: rules when it holds, and defaultRules when it does not.

func Unless

func Unless(condition func(Data) bool, rules string, defaultRules ...string) *ConditionalRules

Unless is When with the condition turned around.

func When

func When(condition func(Data) bool, rules string, defaultRules ...string) *ConditionalRules

When returns the rules an attribute gets when a condition holds, and the ones it gets when it does not.

func (*ConditionalRules) DefaultRules

func (c *ConditionalRules) DefaultRules(data Data) []string

DefaultRules returns the rules used when the condition does not hold.

func (*ConditionalRules) Passes

func (c *ConditionalRules) Passes(data Data) bool

Passes reports whether the condition holds for this request. A nil condition does not hold.

func (*ConditionalRules) Rules

func (c *ConditionalRules) Rules(data Data) []string

Rules returns the rules used when the condition holds.

type CurrentPasswordChecker

type CurrentPasswordChecker interface {
	// CheckCurrentPassword reports whether the plain password is the one the
	// subject the Grant was issued to already has. It answers false for a
	// subject that is not authenticated.
	CheckCurrentPassword(ctx context.Context, g auth.Grant, guard, password string) bool
}

CurrentPasswordChecker is what `current_password` asks. Nothing here resolves a guard or a hasher: the question arrives already answerable.

type Data

type Data map[string]any

Data is a submitted request, one entry per input name, and it is what a Validator is built over.

A value is one of: nil -- the null a browser cannot send but a JSON body can --, a string, a []any (what a multi-select or a repeated input sends), a Data (a nested object), or a File. A number or a bool put in by hand is read as its printed form by every rule that asks.

This is the shape `array`, `list`, `distinct`, `contains`, `nullable` and the file rules need, and the reason the package does not stop at url.Values: url.Values cannot hold a null, a nested value or an upload, and six of the rules are about exactly those.

func DataFrom

func DataFrom(values url.Values) Data

DataFrom builds the Data of a submitted HTML form.

A name sent once is a string and a name sent more than once is a list: reading `tags[]` as a string would make `array` unable to pass on input that really is one. A name present with no value at all is the empty list, which `required` refuses for having no members.

func ExtractDataFromPath

func ExtractDataFromPath(attribute string, masterData Data) Data

ExtractDataFromPath returns the sub-section of the request one dotted path names, so that the rest of it is not walked.

func (Data) Clone

func (d Data) Clone() Data

Clone is a shallow copy, so that a Validator cannot write through to the request's own map.

func (Data) Forget

func (d Data) Forget(key string)

Forget drops the key. Only a top-level key is dropped, which is every key an exclude rule names.

func (Data) Get

func (d Data) Get(key string) any

Get returns the value at the key, and nil when there is none.

func (Data) Has

func (d Data) Has(key string) bool

Has reports whether the key exists, dot notation included.

func (Data) HasAll

func (d Data) HasAll(keys []string) bool

HasAll reports whether every one of the keys exists. It is what missing_with_all and present_with_all ask.

func (Data) HasAny

func (d Data) HasAny(keys []string) bool

HasAny reports whether at least one of the keys exists. It is what required_with, missing_with and present_with ask.

func (Data) Values

func (d Data) Values() url.Values

Values renders the Data back as a submitted form, for a caller that hands the whole thing on. A value that is not text -- a nested Data, an upload -- has no spelling in url.Values and is left out.

type DataAwareRule

type DataAwareRule interface {
	// SetData hands the rule every value being validated.
	SetData(data Data)
}

DataAwareRule is implemented by a rule object that has to see the whole request and not only the one value it was written against -- a check that compares two fields, for instance. Before asking such a rule whether the value passes, the validator hands it every value it is validating. A rule that looks only at its own value does not implement it and is not given the data.

type DatabasePresenceVerifier

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

DatabasePresenceVerifier is the PresenceVerifier over a relational store.

The query is given rather than built -- see PresenceQuery -- so what is left here is the two counts and the connection name.

func NewDatabasePresenceVerifier

func NewDatabasePresenceVerifier(query PresenceQuery) *DatabasePresenceVerifier

NewDatabasePresenceVerifier returns a verifier that counts through the given query.

func (*DatabasePresenceVerifier) Connection

func (d *DatabasePresenceVerifier) Connection() string

Connection reports the connection SetConnection named, which is what the query is expected to run on.

func (*DatabasePresenceVerifier) GetCount

func (d *DatabasePresenceVerifier) GetCount(ctx context.Context, g auth.Grant, collection, column string,
	value any, excludeID any, idColumn string, extra map[string]string) (int, error)

GetCount counts the rows holding the value, through the query this was built with.

func (*DatabasePresenceVerifier) GetMultiCount

func (d *DatabasePresenceVerifier) GetMultiCount(ctx context.Context, g auth.Grant, collection, column string,
	values []any, extra map[string]string) (int, error)

GetMultiCount counts the rows holding any of the values, through the same query.

func (*DatabasePresenceVerifier) SetConnection

func (d *DatabasePresenceVerifier) SetConnection(connection string)

SetConnection names the connection the query is expected to run on.

type DatabaseRule

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

DatabaseRule holds what the two database rules have in common: the table and the column to look in, the extra conditions that narrow the search, and the query callbacks registered through Using. Unique and Exists embed it rather than repeat it, so the Where methods below are written once and read as methods on either; FormatWheres renders the conditions into the parameter list each of them builds. It is not useful on its own.

func (*DatabaseRule) FormatWheres

func (r *DatabaseRule) FormatWheres() string

FormatWheres renders the conditions into the trailing parameters of the rule, column then value, every value quoted.

func (*DatabaseRule) OnlyTrashed

func (r *DatabaseRule) OnlyTrashed(deletedAtColumn ...string) *DatabaseRule

OnlyTrashed narrows the search to rows that are soft deleted. The column defaults to deleted_at.

func (*DatabaseRule) QueryCallbacks

func (r *DatabaseRule) QueryCallbacks() []func(query any)

QueryCallbacks returns the callbacks Using registered.

func (*DatabaseRule) ResolveTableName

func (r *DatabaseRule) ResolveTableName(table string) string

ResolveTableName returns the table a rule queries. There are no models here, so a table name is already the table name.

func (*DatabaseRule) Using

func (r *DatabaseRule) Using(callback func(query any)) *DatabaseRule

Using registers a query callback the rule runs instead of the conditions it would otherwise build.

The query is any because the builder belongs to hesape/database and this package does not import it -- a rule carries the callback and the repository that runs it knows what it holds.

func (*DatabaseRule) Where

func (r *DatabaseRule) Where(column, value string) *DatabaseRule

Where narrows the search to rows holding value in column.

func (*DatabaseRule) WhereIn

func (r *DatabaseRule) WhereIn(column string, values ...string) *DatabaseRule

WhereIn narrows the search to rows holding one of the values in column.

func (*DatabaseRule) WhereNot

func (r *DatabaseRule) WhereNot(column, value string) *DatabaseRule

WhereNot narrows the search to rows NOT holding value in column.

func (*DatabaseRule) WhereNotIn

func (r *DatabaseRule) WhereNotIn(column string, values ...string) *DatabaseRule

WhereNotIn narrows the search to rows holding none of the values in column.

func (*DatabaseRule) WhereNotNull

func (r *DatabaseRule) WhereNotNull(column string) *DatabaseRule

WhereNotNull narrows the search to rows holding anything but null in column.

func (*DatabaseRule) WhereNull

func (r *DatabaseRule) WhereNull(column string) *DatabaseRule

WhereNull narrows the search to rows holding null in column.

func (*DatabaseRule) WithoutTrashed

func (r *DatabaseRule) WithoutTrashed(deletedAtColumn ...string) *DatabaseRule

WithoutTrashed narrows the search to rows that are not soft deleted. The column defaults to deleted_at.

type Date

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

Date builds the date rules of one field, without the caller remembering their names. Build one with NewDate.

func NewDate

func NewDate() *Date

NewDate returns a Date carrying no constraint yet.

func (*Date) After

func (r *Date) After(date string) *Date

After adds after, against the given moment.

func (*Date) AfterOrEqual

func (r *Date) AfterOrEqual(date string) *Date

AfterOrEqual adds after_or_equal, against the given moment.

func (*Date) AfterToday

func (r *Date) AfterToday() *Date

AfterToday adds after:today.

func (*Date) Before

func (r *Date) Before(date string) *Date

Before adds before, against the given moment.

func (*Date) BeforeOrEqual

func (r *Date) BeforeOrEqual(date string) *Date

BeforeOrEqual adds before_or_equal, against the given moment.

func (*Date) BeforeToday

func (r *Date) BeforeToday() *Date

BeforeToday adds before:today.

func (*Date) Between

func (r *Date) Between(from, to string) *Date

Between adds after and before, so both ends are exclusive.

func (*Date) BetweenOrEqual

func (r *Date) BetweenOrEqual(from, to string) *Date

BetweenOrEqual adds after_or_equal and before_or_equal, so both ends are inclusive.

func (*Date) Format

func (r *Date) Format(format string) *Date

Format sets the layout the value has to be written in, which makes the rule date_format rather than date. It is a GO layout -- Format("2006-01-02"), never "Y-m-d".

func (*Date) String

func (r *Date) String() string

String renders the chain, starting with date or with date_format.

func (*Date) TodayOrAfter

func (r *Date) TodayOrAfter() *Date

TodayOrAfter adds after_or_equal:today.

func (*Date) TodayOrBefore

func (r *Date) TodayOrBefore() *Date

TodayOrBefore adds before_or_equal:today.

type Dimensioner

type Dimensioner interface {
	// Dimensions returns the pixel size of an image, and false when the bytes
	// are not one.
	Dimensions() (width, height int, ok bool)
}

Dimensioner is a File that knows its own pixel size. One that implements it is measured through it; one that does not has its bytes decoded instead.

type Dimensions

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

Dimensions builds the "dimensions" rule: the pixel width and height an uploaded image must have or stay within, and the aspect ratio it must match. Each method sets one constraint, and String renders them all as a single rule in a fixed order rather than the order they were set, since a Go map remembers none. Build one with NewDimensions.

func NewDimensions

func NewDimensions() *Dimensions

NewDimensions returns a Dimensions carrying no constraint yet.

func (*Dimensions) Height

func (r *Dimensions) Height(value int) *Dimensions

Height sets the exact height, in pixels.

func (*Dimensions) MaxHeight

func (r *Dimensions) MaxHeight(value int) *Dimensions

MaxHeight sets the highest height, in pixels.

func (*Dimensions) MaxRatio

func (r *Dimensions) MaxRatio(value float64) *Dimensions

MaxRatio sets the highest aspect ratio.

func (*Dimensions) MaxWidth

func (r *Dimensions) MaxWidth(value int) *Dimensions

MaxWidth sets the highest width, in pixels.

func (*Dimensions) MinHeight

func (r *Dimensions) MinHeight(value int) *Dimensions

MinHeight sets the lowest height, in pixels.

func (*Dimensions) MinRatio

func (r *Dimensions) MinRatio(value float64) *Dimensions

MinRatio sets the lowest aspect ratio.

func (*Dimensions) MinWidth

func (r *Dimensions) MinWidth(value int) *Dimensions

MinWidth sets the lowest width, in pixels.

func (*Dimensions) Ratio

func (r *Dimensions) Ratio(value float64) *Dimensions

Ratio sets the exact aspect ratio, width over height.

func (*Dimensions) RatioBetween

func (r *Dimensions) RatioBetween(min, max float64) *Dimensions

RatioBetween sets both ends of the aspect ratio.

func (*Dimensions) String

func (r *Dimensions) String() string

String renders the constraints as one rule, in dimensionOrder.

func (*Dimensions) Width

func (r *Dimensions) Width(value int) *Dimensions

Width sets the exact width, in pixels.

type EmailRule

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

EmailRule builds the "email" rule together with the list of checks it should run: the RFC reading, a stricter one, an MX record lookup on the domain, the spoofing check, and the native one. With nothing asked for it renders the bare "email", which is the shape check alone. Build one with NewEmailRule. It carries the suffix because Email is already the one-value helper here.

func NewEmailRule

func NewEmailRule() *EmailRule

NewEmailRule returns an EmailRule asking for the shape check alone.

func (*EmailRule) PreventSpoofing

func (r *EmailRule) PreventSpoofing() *EmailRule

PreventSpoofing asks for the spoofing check.

func (*EmailRule) RfcCompliant

func (r *EmailRule) RfcCompliant(strict ...bool) *EmailRule

RfcCompliant asks for the RFC reading, or for the stricter one when strict is true.

func (*EmailRule) Rules

func (r *EmailRule) Rules(rules ...string) *EmailRule

Rules merges more checks into the ones this asks for.

func (*EmailRule) Strict

func (r *EmailRule) Strict() *EmailRule

Strict asks for the stricter reading.

func (*EmailRule) String

func (r *EmailRule) String() string

String renders the chain. With nothing asked for it is the bare `email`, which is the shape check.

func (*EmailRule) ValidateMxRecord

func (r *EmailRule) ValidateMxRecord() *EmailRule

ValidateMxRecord asks for the lookup on the domain.

func (*EmailRule) WithNativeValidation

func (r *EmailRule) WithNativeValidation(allowUnicode ...bool) *EmailRule

WithNativeValidation asks for the native check, allowing unicode when told to.

type Enum

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

Enum is the rule that the value must be one of the cases of a type.

Only and Except then narrow them. Build it with EnumOf, which reads the cases off the type, or with NewEnum when there is no type to read -- a set that lives in a column and nowhere else.

func EnumOf added in v0.25.0

func EnumOf[E enum.Enum](values ...E) *Enum

EnumOf returns an Enum over the cases of a generated enum type.

validation.EnumOf(enums.InvoiceStatusValues()...)

The cases are derived rather than written, so the rule cannot disagree with the type -- which is the failure a hand-written list produces and nothing reports. It takes the values because the generated list of them is a package function, the one place that knows the declaration order.

func NewEnum

func NewEnum(cases ...string) *Enum

NewEnum returns an Enum over the given cases.

The cases are text, so the rule can only compare text. Prefer EnumOf wherever a generated type exists: a list written beside a type is a second copy of the set, and the two disagree the first time a case is added to one of them.

func (*Enum) Except

func (r *Enum) Except(values ...string) *Enum

Except narrows the cases by removing these.

func (*Enum) Message

func (r *Enum) Message() []string

Message returns the sentence for a value outside the cases.

func (*Enum) Only

func (r *Enum) Only(values ...string) *Enum

Only narrows the cases to these, and nothing else passes.

func (*Enum) Passes

func (r *Enum) Passes(attribute string, value any) bool

Passes reports whether the value is one of the cases, after Only and Except have narrowed them.

A value of the enum type is asked rather than compared: stringOf renders a named type as nothing, so comparing text used to refuse every typed value the rule was built for. Only and Except still narrow, and they narrow on the shown spelling, which is what the caller wrote them in.

func (*Enum) SetValidator

func (r *Enum) SetValidator(validator *Validator)

SetValidator hands the rule the validator running it, so that its message comes from the same translator.

type Errors

type Errors map[string][]string

Errors maps a field to its messages. It is what a view draws its inline errors from, and it serializes straight into the HTMX partial that re-renders the form.

func (Errors) Add

func (e Errors) Add(field, msg string)

Add appends a message to a field. It is a no-op on a nil map, so a caller that forgot to initialize the map does not panic in the middle of a request.

func (Errors) AddIf

func (e Errors) AddIf(condition bool, field, msg string) Errors

AddIf appends the message only when the condition holds.

func (Errors) All

func (e Errors) All() []string

All returns every message in the bag, with the fields in a stable order so that two renders of the same failure agree.

A Go map remembers no order, so the fields are sorted.

func (Errors) Any

func (e Errors) Any() bool

Any reports whether anything failed.

func (Errors) Count

func (e Errors) Count() int

Count returns the number of messages, not the number of fields.

func (Errors) Error

func (e Errors) Error() string

Error renders the errors with fields in a stable order, so that logs and golden files do not change between runs.

func (Errors) First

func (e Errors) First(key ...string) string

First returns the first message for a key, or the first message of any key when none is given.

Only the first key of the variadic is read. A key with no messages is the empty string.

With no key the field order is not stable -- a map has none -- so that form answers "what is wrong" and not "which field". When which field failed matters, pass the key.

func (Errors) Forget

func (e Errors) Forget(key string) Errors

Forget drops every message for a key.

func (Errors) Get

func (e Errors) Get(key string) []string

Get returns every message for a key.

An absent key is an empty slice. There is no wildcard form: this bag keys by the field name the validator wrote, and no rule writes a star into one.

func (Errors) GetMessages

func (e Errors) GetMessages() map[string][]string

GetMessages is an alias of Messages.

func (Errors) Has

func (e Errors) Has(keys ...string) bool

Has reports whether every key given has at least one message. No key at all is Any.

func (Errors) HasAny

func (e Errors) HasAny(keys ...string) bool

HasAny reports whether at least one of the keys has a message.

func (Errors) IsEmpty

func (e Errors) IsEmpty() bool

IsEmpty reports whether nothing failed.

func (Errors) IsNotEmpty

func (e Errors) IsNotEmpty() bool

IsNotEmpty reports whether anything failed.

func (Errors) Keys

func (e Errors) Keys() []string

Keys returns the fields that have a message, sorted for the reason All is.

func (Errors) Merge

func (e Errors) Merge(other Errors) Errors

Merge appends the messages of other to the ones already here.

func (Errors) Messages

func (e Errors) Messages() map[string][]string

Messages returns the raw map.

func (Errors) Missing

func (e Errors) Missing(keys ...string) bool

Missing reports whether none of the keys has a message.

func (Errors) Unique

func (e Errors) Unique() []string

Unique is All with the repeats dropped, keeping the first of each.

type ExcludeIf

type ExcludeIf struct{ Condition bool }

ExcludeIf renders the "exclude" rule when its condition holds and an empty string when it does not, so a field can be dropped from a rule set without branching around the set itself. An excluded attribute is removed from the validated data rather than reported: it is not part of this request, which is a different thing from being wrong, and no message is put on it. Build one with NewExcludeIf.

func NewExcludeIf

func NewExcludeIf(condition bool) *ExcludeIf

NewExcludeIf returns an ExcludeIf over a condition already settled. A caller holding a closure calls it: the condition is a bool, so nothing else can be passed by mistake.

func (*ExcludeIf) String

func (r *ExcludeIf) String() string

String renders `exclude` when the condition holds, and nothing at all when it does not.

type Exists

type Exists struct{ DatabaseRule }

Exists builds the "exists" rule: some row in the table must already hold this value in the column, which is how an identifier arriving from a form is checked before anything is written against it. The check is a read of the table, so it runs only with a Grant carrying a tenant and counts only that tenant's rows -- an identifier belonging to somebody else does not exist as far as this rule is concerned. Build one with NewExists.

func NewExists

func NewExists(table string, column ...string) *Exists

NewExists returns an `exists` rule over the table. The column defaults to "NULL", which the rule reads as the attribute's own name.

func (*Exists) String

func (r *Exists) String() string

String renders the rule, with the conditions.

type ExplodedRules

type ExplodedRules struct {
	// Rules is one entry per attribute, holding the rules written against it in
	// the order they were written.
	Rules map[string][]string

	// Order is the order the attributes were written in, because a Go map
	// remembers none.
	Order []string

	// ImplicitAttributes is the wildcard key each expanded attribute came from,
	// which is what names a field in a message the way the caller wrote it.
	ImplicitAttributes map[string][]string
}

ExplodedRules is a rule set with every wildcard expanded, together with the wildcard keys that produced it.

type ExtensionFunc

type ExtensionFunc func(v *Validator, attribute string, value any, parameters []string) bool

ExtensionFunc is a rule written by the application, called with the arguments every rule in the catalogue is.

type Factory

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

Factory makes Validators already wired to the translator, the presence verifier and the message overrides an application registered once.

It is a value an application builds at boot and hands where it is needed. There is no global one, and nothing here resolves an extension by name: an extension is a function, and Extend takes it directly.

func NewFactory

func NewFactory(translator Translator) *Factory

NewFactory returns a Factory over a translator. The translator may be nil, and then every Validator it makes falls back to the compiled rule set's own sentences -- see getMessage.

func (*Factory) ExcludeUnvalidatedArrayKeys

func (f *Factory) ExcludeUnvalidatedArrayKeys()

ExcludeUnvalidatedArrayKeys drops the keys of an array that no rule declared.

func (*Factory) Extend

func (f *Factory) Extend(rule string, extension ExtensionFunc, message ...string)

Extend registers a rule this application adds, and the sentence it says when it fails.

It registers into the one catalogue MustCompile checks against, so the name is real for every rule set compiled after this call -- which is why it belongs at start-up, before any set is compiled.

func (*Factory) ExtendDependent

func (f *Factory) ExtendDependent(rule string, extension ExtensionFunc, message ...string)

ExtendDependent registers a rule whose first parameter names another field.

func (*Factory) ExtendImplicit

func (f *Factory) ExtendImplicit(rule string, extension ExtensionFunc, message ...string)

ExtendImplicit registers a rule that runs even when the attribute is blank, the way `required` does.

func (*Factory) GetPresenceVerifier

func (f *Factory) GetPresenceVerifier() PresenceVerifier

GetPresenceVerifier returns the verifier this Factory hands its Validators.

func (*Factory) GetTranslator

func (f *Factory) GetTranslator() Translator

GetTranslator returns the catalogue this Factory hands its Validators.

func (*Factory) IncludeUnvalidatedArrayKeys

func (f *Factory) IncludeUnvalidatedArrayKeys()

IncludeUnvalidatedArrayKeys keeps the keys of an array that no rule declared.

func (*Factory) Make

func (f *Factory) Make(data Data, rules *Set, opts ...ValidatorOption) *Validator

Make returns a Validator over the data and the rules, with everything this Factory carries already on it.

The rules are a compiled Set rather than an array of strings, because the strings are parsed and checked at boot -- see MustCompile. Per-call overrides are the WithCustomMessages and WithCustomAttributes options.

func (*Factory) Replacer

func (f *Factory) Replacer(rule string, replacer ReplacerFunc)

Replacer registers how one rule fills the placeholders of its own message.

func (*Factory) Resolver

func (f *Factory) Resolver(resolver func(data Data, rules *Set, opts []ValidatorOption) *Validator)

Resolver registers another way of building the Validator itself, which is how an application ships one of its own.

func (*Factory) SetAttributeNames

func (f *Factory) SetAttributeNames(attributes map[string]string) *Factory

SetAttributeNames sets the field names every Validator this Factory makes starts with.

func (*Factory) SetCustomMessages

func (f *Factory) SetCustomMessages(messages map[string]any) *Factory

SetCustomMessages sets the inline messages every Validator this Factory makes starts with.

func (*Factory) SetPresenceVerifier

func (f *Factory) SetPresenceVerifier(g auth.Grant, presenceVerifier PresenceVerifier)

SetPresenceVerifier sets the Grant and the verifier this Factory hands its Validators.

func (*Factory) Validate

func (f *Factory) Validate(data Data, rules *Set, opts ...ValidatorOption) (Input, error)

Validate is Make and Validate in one call.

type File

type File interface {
	// GetPath returns the non-empty location or upload name that identifies the
	// file. An empty path is a file that was never written, which `mimes` and
	// `mimetypes` refuse.
	GetPath() string
	// GetRealPath returns where the bytes are.
	GetRealPath() string
	// GetSize returns the size in bytes. The size rules divide it by 1024,
	// because `max:100` on a file means kilobytes.
	GetSize() int64
	// GetMimeType returns the media type, guessed from the content.
	GetMimeType() string
	// GetExtension returns the extension of the file's own name.
	GetExtension() string
	// GuessExtension returns the extension implied by the content, which is what
	// `mimes` compares and why a .png renamed to .jpg does not pass.
	GuessExtension() string
}

File is the shape `file`, `mimes`, `mimetypes`, `extensions`, `dimensions` and the size rules ask about an upload.

It is an interface rather than a struct because the upload itself belongs to the HTTP layer. A type there satisfies this by having the methods, with no import either way.

type FileRule

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

FileRule builds the rules an upload has to pass: what it may be, and how big it may be. It carries the suffix because File is already the upload interface. Build one with NewFileRule, or with NewImageFile.

func NewFileRule

func NewFileRule() *FileRule

NewFileRule returns a FileRule that asks only for an upload that finished.

func NewImageFile

func NewImageFile(allowSvg ...bool) *FileRule

NewImageFile returns a FileRule that asks for an image, and for an SVG too when allowSvg is true.

func Types

func Types(mimetypes ...string) *FileRule

Types returns a FileRule over the media types or extensions the upload may be.

func (*FileRule) Between

func (r *FileRule) Between(minSize, maxSize int) *FileRule

Between sets both ends of the size, in kilobytes.

func (*FileRule) Dimensions

func (r *FileRule) Dimensions(dimensions *Dimensions) *FileRule

Dimensions adds the dimension rule the builder rendered.

func (*FileRule) Extensions

func (r *FileRule) Extensions(extensions ...string) *FileRule

Extensions sets the extensions the upload may carry. It is the extension the BROWSER sent, which is not the same question the content asks.

func (*FileRule) Max

func (r *FileRule) Max(size int) *FileRule

Max sets the largest size, in kilobytes.

func (*FileRule) Min

func (r *FileRule) Min(size int) *FileRule

Min sets the smallest size, in kilobytes.

func (*FileRule) Rules

func (r *FileRule) Rules(rules ...string) *FileRule

Rules merges more rules into the ones this builds.

func (*FileRule) Size

func (r *FileRule) Size(size int) *FileRule

Size sets the exact size, in kilobytes.

func (*FileRule) String

func (r *FileRule) String() string

String renders the chain: what the upload may be, then how big it may be, then whatever Rules added.

type ImplicitRule

type ImplicitRule interface {
	// Implicit reports whether this rule runs on a blank value.
	Implicit() bool
}

ImplicitRule is a rule that runs even when the value is blank. The marker is the question itself, because an interface here needs a method.

type In

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

In builds the "in" rule -- the value must be one of a fixed list -- without the caller spelling the rule string by hand. String renders every value quoted, so one containing a comma survives the parameter list instead of splitting into two allowed values. Build one with NewIn.

func NewIn

func NewIn(values ...string) *In

NewIn returns an `in` rule over the given values.

func (*In) String

func (r *In) String() string

String renders the rule, every value quoted.

type Input

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

Input is what passed the rules. It is the only way to read a submitted value out of a validated request: a field the set does not declare is not in here, so a value nobody wrote a rule for cannot reach a repository by accident.

func (Input) Bool

func (in Input) Bool(field string) bool

Bool reads a checkbox.

It accepts every value `accepted` does -- "1", "on", "yes", "true" -- and not only the "0" and "1" that the `boolean` rule allows, because an unticked checkbox sends nothing and a ticked one sends "on". Anything else is false: a checkbox has no third answer.

func (Input) Data

func (in Input) Data() Data

Data returns a copy of everything that passed, in the shape the rules read it. It is a copy so that a caller cannot reach back into the request's own values through it.

func (Input) File

func (in Input) File(field string) (File, bool)

File returns the upload that passed, and false when the field holds none.

func (Input) Float

func (in Input) Float(field string) float64

Float returns the value as a number, zero when there is none. `numeric` or `decimal` is what proves there is.

func (Input) Has

func (in Input) Has(field string) bool

Has reports whether the field was sent and passed.

func (Input) Int

func (in Input) Int(field string) int64

Int returns the value as a whole number. It is zero when the field was not sent or does not hold one -- which is a rule's job to have proven, with `integer`.

func (Input) String

func (in Input) String(field string) string

String returns the value, or empty when the field was not sent. A field sent more than once is a list, and this is its first value -- what url.Values.Get answers.

func (Input) Strings

func (in Input) Strings(field string) []string

Strings returns every value of a field, which is what a multi-select sends. A field sent once is a list of one.

func (Input) Time

func (in Input) Time(field, layout string) time.Time

Time reads a value with the layout its rule declared. It is the zero time when the field was not sent or does not parse, which `date_format` is what prevents.

func (Input) Values

func (in Input) Values() url.Values

Values returns a copy of everything that passed, as a submitted form, for a caller that hands the whole thing on.

type InvokableValidationRule

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

InvokableValidationRule is a ValidationRule wrapped so that it reads as a Rule. Build one with NewInvokableValidationRule.

func NewInvokableValidationRule

func NewInvokableValidationRule(invokable ValidationRule) *InvokableValidationRule

NewInvokableValidationRule wraps a ValidationRule as a Rule, carrying over whether the wrapped one is implicit.

The name is New rather than Make because Make is already the Validator's constructor.

func (*InvokableValidationRule) Implicit

func (r *InvokableValidationRule) Implicit() bool

Implicit reports whether the wrapped rule runs on a blank value.

func (*InvokableValidationRule) Invokable

func (r *InvokableValidationRule) Invokable() ValidationRule

Invokable returns the rule this wraps.

func (*InvokableValidationRule) Message

func (r *InvokableValidationRule) Message() []string

Message returns what the last Passes collected.

func (*InvokableValidationRule) Passes

func (r *InvokableValidationRule) Passes(attribute string, value any) bool

Passes runs the wrapped rule, handing it the data and the validator first when it asks for them, and reports whether it called fail. A nil rule passes.

func (*InvokableValidationRule) SetData

func (r *InvokableValidationRule) SetData(data Data)

SetData hands the wrapper every value being validated.

func (*InvokableValidationRule) SetValidator

func (r *InvokableValidationRule) SetValidator(validator *Validator)

SetValidator hands the wrapper the validator running it.

type Messages

type Messages map[string]string

Messages overrides the sentence one rule puts on one field, keyed "field.rule":

validation.MustCompile(rules, validation.WithMessageOverrides(validation.Messages{
	"email.required": "we need an address to send the receipt to",
}))

A key naming a field or a rule the set does not declare is a boot failure: a typo in an override is otherwise invisible, because the default sentence is still there and still reads correctly.

type NestedRules

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

NestedRules is the rules for one member of an array, decided by looking at that member. Build one with NewNestedRules.

func ForEach

func ForEach(callback func(value any, attribute string, data Data) Rules) *NestedRules

ForEach returns rules for one member of an array, decided by looking at that member.

func NewNestedRules

func NewNestedRules(callback func(value any, attribute string, data Data) Rules) *NestedRules

NewNestedRules returns rules the callback decides, member by member.

func (*NestedRules) Compile

func (n *NestedRules) Compile(attribute string, value any, data Data) *ExplodedRules

Compile asks the callback what this member's rules are, and expands them. A nil callback compiles to nothing.

type NotIn

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

NotIn builds the "not_in" rule -- the value must be none of a fixed list -- with the same quoting In uses, so a listed value containing a comma is still one value. Build one with NewNotIn.

func NewNotIn

func NewNotIn(values ...string) *NotIn

NewNotIn returns a `not_in` rule over the given values.

func (*NotIn) String

func (r *NotIn) String() string

String renders the rule, every value quoted.

type Numeric

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

Numeric builds a whole chain of numeric rules for one field -- bounds, digit counts, decimal places, comparisons against another field -- without the caller remembering any of their names. It always begins with "numeric", and String renders the chain with repeats dropped, because several of the methods add "integer" of their own. Build one with NewNumeric.

func NewNumeric

func NewNumeric() *Numeric

NewNumeric returns a Numeric carrying `numeric` and nothing else yet.

func (*Numeric) Between

func (r *Numeric) Between(min, max float64) *Numeric

Between adds between, both bounds inclusive.

func (*Numeric) Decimal

func (r *Numeric) Decimal(min int, max ...int) *Numeric

Decimal adds decimal: exactly min places, or between min and max of them.

func (*Numeric) Different

func (r *Numeric) Different(field string) *Numeric

Different adds different, against the named field.

func (*Numeric) Digits

func (r *Numeric) Digits(length int) *Numeric

Digits adds integer and digits: exactly this many of them.

func (*Numeric) DigitsBetween

func (r *Numeric) DigitsBetween(min, max int) *Numeric

DigitsBetween adds integer and digits_between.

func (*Numeric) Exactly

func (r *Numeric) Exactly(value int) *Numeric

Exactly adds integer and size.

func (*Numeric) GreaterThan

func (r *Numeric) GreaterThan(field string) *Numeric

GreaterThan adds gt, against the named field.

func (*Numeric) GreaterThanOrEqualTo

func (r *Numeric) GreaterThanOrEqualTo(field string) *Numeric

GreaterThanOrEqualTo adds gte, against the named field.

func (*Numeric) Integer

func (r *Numeric) Integer() *Numeric

Integer adds integer.

func (*Numeric) LessThan

func (r *Numeric) LessThan(field string) *Numeric

LessThan adds lt, against the named field.

func (*Numeric) LessThanOrEqualTo

func (r *Numeric) LessThanOrEqualTo(field string) *Numeric

LessThanOrEqualTo adds lte, against the named field.

func (*Numeric) Max

func (r *Numeric) Max(value float64) *Numeric

Max adds max.

func (*Numeric) MaxDigits

func (r *Numeric) MaxDigits(value int) *Numeric

MaxDigits adds max_digits.

func (*Numeric) Min

func (r *Numeric) Min(value float64) *Numeric

Min adds min.

func (*Numeric) MinDigits

func (r *Numeric) MinDigits(value int) *Numeric

MinDigits adds min_digits.

func (*Numeric) MultipleOf

func (r *Numeric) MultipleOf(value float64) *Numeric

MultipleOf adds multiple_of.

func (*Numeric) Same

func (r *Numeric) Same(field string) *Numeric

Same adds same, against the named field.

func (*Numeric) String

func (r *Numeric) String() string

String renders the chain, with the repeats dropped -- Digits and Exactly both add `integer`.

type Option

type Option func(*settings)

Option adjusts a compilation.

func WithMessageOverrides

func WithMessageOverrides(m Messages) Option

WithMessageOverrides replaces the default sentence for the named field and rule.

It is not spelled WithMessages: that name belongs to WithMessages on a ValidationException, and this is a compile-time override of a sentence rather than an exception built out of one.

type Password

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

Password is the password policy of one field: a length, the kinds of character it must carry, and whether it has appeared in a leak.

It cannot be a rule string: it holds a verifier, and it says four different things depending on which part failed. It runs through Validator.After:

v.After(func(v *validation.Validator) {
	v.ValidateUsingCustomRule("password", v.GetValue("password"), rule)
})

func NewPassword

func NewPassword(min int) *Password

NewPassword returns a policy asking for a minimum length. A minimum below one is raised to one.

func PasswordDefault

func PasswordDefault() *Password

PasswordDefault returns the registered policy, or a minimum of eight when none was registered.

func PasswordMin

func PasswordMin(size int) *Password

PasswordMin returns a policy asking for a minimum length.

func PasswordRequired

func PasswordRequired() (string, *Password)

PasswordRequired returns the `required` rule string and the default policy, which are the two things a required password field takes.

func PasswordSometimes

func PasswordSometimes() (string, *Password)

PasswordSometimes returns the `sometimes` rule string and the default policy.

func (*Password) AppliedRules

func (p *Password) AppliedRules() map[string]any

AppliedRules returns what this policy is currently asking for, which is what a "your password must" list on the form is drawn from.

func (*Password) Letters

func (p *Password) Letters() *Password

Letters asks for at least one letter.

func (*Password) Max

func (p *Password) Max(size int) *Password

Max sets the longest the password may be.

func (*Password) Message

func (p *Password) Message() []string

Message returns what the last Passes refused, one sentence per failed check.

func (*Password) MixedCase

func (p *Password) MixedCase() *Password

MixedCase asks for at least one letter of each case.

func (*Password) Numbers

func (p *Password) Numbers() *Password

Numbers asks for at least one number.

func (*Password) Passes

func (p *Password) Passes(attribute string, value any) bool

Passes reports whether the value satisfies the policy, and Message says what failed when it does not.

The length and the merged rules are checked by a sibling validator compiled from the same chain; the four character checks run after it.

func (*Password) Rules

func (p *Password) Rules(rules ...string) *Password

Rules merges more rules into the ones this policy enforces.

func (*Password) SetData

func (p *Password) SetData(data Data)

SetData hands the rule the data being validated.

func (*Password) SetValidator

func (p *Password) SetValidator(validator *Validator)

SetValidator hands the rule the validator running it, so that its messages come from the same translator.

func (*Password) Symbols

func (p *Password) Symbols() *Password

Symbols asks for at least one symbol.

func (*Password) Uncompromised

func (p *Password) Uncompromised(verifier UncompromisedVerifier, threshold ...int) *Password

Uncompromised refuses a password that has appeared in a data leak more times than the threshold allows.

The verifier is a parameter because nothing here resolves one. A nil verifier FAILS the check rather than passing it: "we could not ask" is not "it is safe".

type PresenceQuery

type PresenceQuery func(ctx context.Context, g auth.Grant, collection, column string, values []any,
	excludeID any, idColumn string, extra map[string]string) (int, error)

PresenceQuery is the one question `unique` and `exists` ask of a store: how many rows of the collection hold any of the values in the column, once the Grant's tenant and the extra conditions are applied.

It is a function rather than a repository because building the query needs hesape/database, and this package does not import it: the application passes the query in, already written against whatever it stores rows in.

type PresenceVerifier

type PresenceVerifier interface {
	// GetCount returns how many rows of the collection hold the value in the
	// column, excluding the row whose idColumn is excludeID when one is given,
	// and matching every extra condition.
	GetCount(ctx context.Context, g auth.Grant, collection, column string, value any, excludeID any, idColumn string, extra map[string]string) (int, error)

	// GetMultiCount returns how many rows hold any of the values.
	GetMultiCount(ctx context.Context, g auth.Grant, collection, column string, values []any, extra map[string]string) (int, error)
}

PresenceVerifier is what `exists` and `unique` count rows through. Both methods take the context the query runs under and the Grant that authorizes it.

type ProhibitedIf

type ProhibitedIf struct{ Condition bool }

ProhibitedIf renders the "prohibited" rule when its condition holds and an empty string when it does not. A prohibited attribute fails whenever it arrived with a value at all, which refuses a field that must not accompany this request instead of quietly ignoring it. Build one with NewProhibitedIf.

func NewProhibitedIf

func NewProhibitedIf(condition bool) *ProhibitedIf

NewProhibitedIf returns a ProhibitedIf over a condition already settled.

func (*ProhibitedIf) String

func (r *ProhibitedIf) String() string

String renders `prohibited` when the condition holds, and nothing at all when it does not.

type ReplacerFunc

type ReplacerFunc func(message, attribute, rule string, parameters []string, validator *Validator) string

ReplacerFunc is a replacer an application registers with AddReplacer: message, attribute, rule, parameters, validator.

type RequiredIf

type RequiredIf struct{ Condition bool }

RequiredIf renders the "required" rule when its condition holds and an empty string when it does not, so a field is demanded only in the case the caller decides. The condition is a bool settled before the rule set is built; a condition that has to look at the request is required_if, which is a rule name and takes the other field as its parameter. Build one with NewRequiredIf.

func NewRequiredIf

func NewRequiredIf(condition bool) *RequiredIf

NewRequiredIf returns a RequiredIf over a condition already settled.

func (*RequiredIf) String

func (r *RequiredIf) String() string

String renders `required` when the condition holds, and nothing at all when it does not.

type Resolver

type Resolver interface {
	// LookupIPAddr returns the A and AAAA records of the host.
	LookupIPAddr(ctx context.Context, host string) ([]net.IPAddr, error)
}

Resolver is what `active_url` asks whether a host has an address record. *net.Resolver implements it, and net.DefaultResolver is the default.

type Rule

type Rule interface {
	// Passes reports whether the value satisfies the rule.
	Passes(attribute string, value any) bool
	// Message returns what the last Passes refused, one sentence per failure.
	Message() []string
}

Rule is a check written as a type: it reports whether a value passes, and says what was wrong when it does not.

Message returns a slice because both wrappers below return several: one per call to their fail function.

type RuleFactory

type RuleFactory struct{}

RuleFactory gathers the rule builders under one name, so that a rule set reads as a list of rules rather than as a list of constructors:

validation.Rules{
	"role":  validation.RuleFactory{}.In("admin", "member").String(),
	"email": "required|email|" + validation.RuleFactory{}.Unique("users").String(),
}

It is not spelled Rule because that name is the interface a rule of one's own implements. It is an empty struct, and every method returns exactly what the matching NewX returns: a second way of NAMING a constructor, not a second constructor.

func (RuleFactory) AnyOf

func (RuleFactory) AnyOf(sets ...*Set) *AnyOf

AnyOf builds an AnyOf: the value has to satisfy one of the rule sets.

func (RuleFactory) Array

func (RuleFactory) Array(keys ...string) *ArrayRule

Array builds an `array` rule.

func (RuleFactory) Can

func (RuleFactory) Can(allows func(g auth.Grant, ability string, arguments []string, value any) bool, ability string, arguments ...string) *Can

Can builds a Can: the rule passes when the subject the Grant was issued to is allowed the ability against the value.

func (RuleFactory) Date

func (RuleFactory) Date() *Date

Date builds the date rules of one field.

func (RuleFactory) Default

func (RuleFactory) Default() *FileRule

Default builds the file rule an application uses everywhere unless it says otherwise. Nothing stores it: what comes back is a plain builder, and the application keeps the one it made.

func (RuleFactory) Defaults

func (RuleFactory) Defaults(rules ...string) *FileRule

Defaults builds a file rule with those rules already merged in. Nothing stores it either, for the reason Default gives.

func (RuleFactory) Dimensions

func (RuleFactory) Dimensions() *Dimensions

Dimensions builds a `dimensions` rule.

func (RuleFactory) Email

func (RuleFactory) Email() *EmailRule

Email builds an `email` rule.

func (RuleFactory) Enum

func (RuleFactory) Enum(cases ...string) *Enum

Enum builds an `enum` rule.

Go has no enum type to read the cases off, so they are given: a Go program spells an enum as a named string type with a list of values, and that list is what this takes.

func (RuleFactory) ExcludeIf

func (RuleFactory) ExcludeIf(condition bool) *ExcludeIf

ExcludeIf builds an ExcludeIf over a condition already settled.

func (RuleFactory) Exists

func (RuleFactory) Exists(table string, column ...string) *Exists

Exists builds an `exists` rule.

func (RuleFactory) File

func (RuleFactory) File() *FileRule

File builds the upload rules of one field.

func (RuleFactory) Image

func (RuleFactory) Image(allowSvg ...bool) *FileRule

Image builds the upload rules of an image field, as ImageFile does.

func (RuleFactory) ImageFile

func (RuleFactory) ImageFile(allowSvg ...bool) *FileRule

ImageFile builds the upload rules of an image field.

func (RuleFactory) In

func (RuleFactory) In(values ...string) *In

In builds an `in` rule.

func (RuleFactory) NotIn

func (RuleFactory) NotIn(values ...string) *NotIn

NotIn builds a `not_in` rule.

func (RuleFactory) Numeric

func (RuleFactory) Numeric() *Numeric

Numeric builds the numeric rules of one field.

func (RuleFactory) Password

func (RuleFactory) Password(min int) *Password

Password builds a password policy with a minimum length.

func (RuleFactory) ProhibitedIf

func (RuleFactory) ProhibitedIf(condition bool) *ProhibitedIf

ProhibitedIf builds a ProhibitedIf over a condition already settled.

func (RuleFactory) RequiredIf

func (RuleFactory) RequiredIf(condition bool) *RequiredIf

RequiredIf builds a RequiredIf over a condition already settled.

func (RuleFactory) Unique

func (RuleFactory) Unique(table string, column ...string) *Unique

Unique builds a `unique` rule.

type Rules

type Rules map[string]string

Rules is the rule set of one request, keyed by the name of the form input -- the same name components.FieldProps.Name carries.

A field's rules are one string, and the whole set is written in one place:

var Register = validation.MustCompile(validation.Rules{
	"name":     "required|max:255",
	"email":    "required|email",
	"password": "required|min:12|confirmed",
})

What a string costs is that a typo in "requried" is not a compiler error. What Compile buys back is that it is a BOOT error, naming the field, the rule and the file -- so a rule set that boots is a rule set whose names are all real, whose regular expressions compile, whose numeric arguments are numbers and whose cross-field references name fields that exist.

func FilterConditionalRules

func FilterConditionalRules(rules map[string]any, data Data) Rules

FilterConditionalRules replaces every ConditionalRules in the set with the rules its condition chose.

type Set

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

Set is a compiled, checked rule set.

Build one in a package-level variable with MustCompile: the rules are then parsed once, at boot, and a set that boots is a set whose names are all real. A Set is read-only once compiled, so one is shared by every request; the per-request state lives in the Validator that Make builds over it.

func Compile

func Compile(r Rules, opts ...Option) (*Set, error)

Compile parses and checks a rule set, and reports everything wrong with it.

Use it where a rule set is built from something that is not a literal. Everywhere else the rule set belongs in a package-level variable, which is what MustCompile is for.

func MustCompile

func MustCompile(r Rules, opts ...Option) *Set

MustCompile is Compile for a package-level variable, which is where a rule set belongs: the check then runs before main does.

var StorePost = validation.MustCompile(validation.Rules{
	"title": "required|max:255",
})

It panics, for the reason view.Register panics: finding out at boot beats finding out from the one request that first exercises the rule. A set compiled inside a handler is checked once per request and reports its typo to whoever triggered it -- `aru doctor` refuses that with rules-not-at-boot, because this package cannot see where it was called from.

func (*Set) Fields

func (s *Set) Fields() []string

Fields returns the input names this set declares, sorted.

func (*Set) RulesFor

func (s *Set) RulesFor(field string) []string

RulesFor returns the rule names written against one field, in the order they were written. It is nil for a field the set does not declare.

func (*Set) Source

func (s *Set) Source() (file string, line int)

Source returns where the set was compiled, which is what a boot failure names.

func (*Set) Validate

func (s *Set) Validate(values url.Values) (Input, Errors)

Validate runs the set over a submitted form and returns what passed and what failed.

It is Make plus Passes for the common case: an HTML form, no upload, no rule that leaves the process. A set with `unique`, `exists`, `current_password` or `active_url` in it goes through Make instead, which is where the Grant, the verifier and the context are given -- those four fail closed here, on purpose: a read is authorized like any other, and a rule set carries no Grant.

The input is url.Values rather than Data because that is what a form arrives as; DataFrom is the conversion, and a name sent twice becomes a list.

The returned Errors is nil when nothing failed. Assigning it to an error interface makes that interface non-nil even so, because the type is not nil -- callers ask Any(), and http.Context.Validate returns a plain nil for exactly this reason.

type Translator

type Translator interface {
	// Get returns the line under the key, or the key itself when there is none.
	Get(key string, replace map[string]any, locale string) any
	// Choice returns the line under the key, in the form that number calls for.
	Choice(key string, number int, replace map[string]any, locale string) string
}

Translator is the catalogue messages are read out of, narrowed to the two methods this package reaches for.

Get returns any because a language line is a string and a key naming a group -- "validation.custom", "validation.attributes", "validation.min" -- is a map. A key with no line answers with the key itself, which is what every "is there a custom message" check here compares against.

The locale is a parameter rather than state, as it is on hesape/translation.Translator: the locale belongs to the request. The empty string means the translator's own default.

type UncompromisedVerifier

type UncompromisedVerifier interface {
	// Verify reports false when the value has appeared more times than the
	// threshold allows.
	Verify(value string, threshold int) bool
}

UncompromisedVerifier answers whether a password has appeared in a data leak.

Asking a breach service is a network call and belongs with whoever owns the HTTP client, so what is here is only the question.

type Unique

type Unique struct {
	DatabaseRule
	// contains filtered or unexported fields
}

Unique builds the "unique" rule: no row in the table may already hold this value in the column. Ignore names the one row allowed to hold it, which is how a form that edits an existing record does not collide with itself. The check is a read of the table, so it runs only with a Grant carrying a tenant and counts only that tenant's rows. Build one with NewUnique.

func NewUnique

func NewUnique(table string, column ...string) *Unique

NewUnique returns a `unique` rule over the table. The column defaults to "NULL", which the rule reads as the attribute's own name.

func (*Unique) Ignore

func (r *Unique) Ignore(id string, idColumn ...string) *Unique

Ignore names the row this check is allowed to find, which is the row being edited.

func (*Unique) IgnoreModel

func (r *Unique) IgnoreModel(key string, idColumn ...string) *Unique

IgnoreModel names the row being edited by its key rather than by the record itself. There are no records to pass here, so it is Ignore under a second name.

func (*Unique) String

func (r *Unique) String() string

String renders the rule, with the ignored row and the conditions.

type UploadedFile

type UploadedFile interface {
	File
	// GetClientOriginalExtension returns the extension of the name the browser
	// sent, which is what `extensions` compares -- the name, not the content.
	GetClientOriginalExtension() string
	// IsValid reports whether the upload finished.
	IsValid() bool
}

UploadedFile is a File that also knows what the client called it and whether the upload finished.

type Validatable

type Validatable interface {
	Validate() Errors
}

Validatable is implemented by every request type that checks itself.

type ValidationException

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

ValidationException is the error a failed Validate carries, and what a controller reads to answer 422.

Errors is the bag, GetStatus the code, GetErrorBag the named bag a redirect flashes it into, and GetRedirectTo where that redirect goes.

func NewValidationException

func NewValidationException(validator *Validator, response ...any) *ValidationException

NewValidationException returns the error a failed run is turned into.

The variadic response is the answer the HTTP layer already prepared, when it prepared one. The bag starts at "default" and is changed with ErrorBag.

func WithMessages

func WithMessages(messages map[string][]string) *ValidationException

WithMessages returns an exception built from a plain map of messages, for a failure that no rule produced.

func (*ValidationException) Error

func (e *ValidationException) Error() string

Error is the summary: the first message, and how many more there were.

func (*ValidationException) ErrorBag

func (e *ValidationException) ErrorBag(errorBag string) *ValidationException

ErrorBag names the bag a redirect flashes the messages into, so that two forms on one page do not draw each other's errors.

func (*ValidationException) Errors

func (e *ValidationException) Errors() map[string][]string

Errors returns every validation message, keyed by the field it belongs to. It is what a controller turns into a 422 body.

func (*ValidationException) GetErrorBag

func (e *ValidationException) GetErrorBag() string

GetErrorBag reads the bag ErrorBag named, for the reason GetStatus exists.

func (*ValidationException) GetRedirectTo

func (e *ValidationException) GetRedirectTo() string

GetRedirectTo reads the URL RedirectTo named, for the reason GetStatus exists.

func (*ValidationException) GetResponse

func (e *ValidationException) GetResponse() any

GetResponse returns the answer the HTTP layer prepared, when it prepared one.

func (*ValidationException) GetStatus

func (e *ValidationException) GetStatus() int

GetStatus reads the code Status set. A field cannot carry the same name as the method that sets it, so the pair is spelled the way GetResponse already is.

func (*ValidationException) RedirectTo

func (e *ValidationException) RedirectTo(url string) *ValidationException

RedirectTo names where the client is sent back to, which is the form it came from.

func (*ValidationException) Status

func (e *ValidationException) Status(status int) *ValidationException

Status sets the HTTP status code to answer with. It returns the exception, so calls chain.

func (*ValidationException) Validator

func (e *ValidationException) Validator() *Validator

Validator returns the run that failed.

type ValidationRule

type ValidationRule interface {
	// Validate checks the value, calling fail once per problem it finds.
	Validate(attribute string, value any, fail func(message string))
}

ValidationRule is a check that reports a problem by calling fail with the sentence to show, rather than by returning false. NewInvokableValidationRule wraps one so that it reads as a Rule.

type ValidationRuleParser

type ValidationRuleParser struct {
	// Data is the request the wildcards are expanded against, because "items.*"
	// means the items that were actually sent.
	Data Data

	// ImplicitAttributes is the wildcard key each expanded attribute came from,
	// filled in as the expansion runs.
	ImplicitAttributes map[string][]string
}

ValidationRuleParser turns a rule set as it was written into the rule set the validator runs: one list of rules per attribute, with every wildcard attribute -- "items.*.price" -- replaced by the concrete keys the request actually carries. It holds the request data because that expansion cannot be decided without it, and it remembers which wildcard each expanded key came from, so a message can name the field the way the caller wrote it.

func NewValidationRuleParser

func NewValidationRuleParser(data Data) *ValidationRuleParser

NewValidationRuleParser returns a parser that expands wildcards against data.

func (*ValidationRuleParser) Explode

func (p *ValidationRuleParser) Explode(rules Rules) *ExplodedRules

Explode turns the rules as they were written into the full rule set the validator runs.

The primary purpose of this parser is to expand any "*" rules to all of the explicit rules needed for the given data. For example the rule names.* would get expanded to names.0, names.1, and so on.

func (*ValidationRuleParser) MergeRules

func (p *ValidationRuleParser) MergeRules(results Rules, attribute, rules string) Rules

MergeRules adds more rules to an attribute that may already have some.

type Validator

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

Validator is one submitted request, the rules written against it, and the answer.

It is built by Make and it is not safe for concurrent use: one belongs to one request. The compiled Set behind it is read-only and is shared by all of them.

The four rules that leave the process -- `unique`, `exists`, `current_password` and `active_url` -- take what they need through an option, and each of them fails closed when the thing it needs was not given.

func Make

func Make(data Data, rules *Set, opts ...ValidatorOption) *Validator

Make returns a Validator over the data and the rules.

The rules are a compiled Set rather than an array of strings, because the strings are parsed and checked at boot -- see MustCompile. The data is copied, so that excluding a field does not reach back into the request's own map.

func (*Validator) AddCustomAttributes

func (v *Validator) AddCustomAttributes(attributes map[string]string) *Validator

AddCustomAttributes merges more field names into the ones in force.

func (*Validator) AddCustomValues

func (v *Validator) AddCustomValues(values map[string]map[string]string) *Validator

AddCustomValues merges more value names into the ones in force.

func (*Validator) AddDependentExtension

func (v *Validator) AddDependentExtension(rule string, extension ExtensionFunc)

AddDependentExtension registers a rule of the application's own whose first parameter names another field, which is then checked at boot like every other cross-field reference.

func (*Validator) AddDependentExtensions

func (v *Validator) AddDependentExtensions(extensions map[string]ExtensionFunc)

AddDependentExtensions registers several dependent rules at once.

func (*Validator) AddExtension

func (v *Validator) AddExtension(rule string, extension ExtensionFunc)

AddExtension registers a rule of the application's own.

A rule name has to exist before MustCompile reads it, so an extension joins the one catalogue every rule set is checked against. Registering the same name twice panics, for the reason two answers to one rule name always do.

The catalogue is package level rather than per-validator: an extension is registered once, at start-up, before any validator is made.

func (*Validator) AddExtensions

func (v *Validator) AddExtensions(extensions map[string]ExtensionFunc)

AddExtensions registers several rules of the application's own at once.

func (*Validator) AddFailure

func (v *Validator) AddFailure(attribute, rule string, parameters []string)

AddFailure records that one rule refused one attribute.

An exclude rule never puts a message on the field: its failure removes the field from the validated data instead, which is the whole of what the five exclude rules do.

func (*Validator) AddImplicitExtension

func (v *Validator) AddImplicitExtension(rule string, extension ExtensionFunc)

AddImplicitExtension registers a rule of the application's own that runs even when the value is blank.

func (*Validator) AddImplicitExtensions

func (v *Validator) AddImplicitExtensions(extensions map[string]ExtensionFunc)

AddImplicitExtensions registers several implicit rules at once.

func (*Validator) AddReplacer

func (v *Validator) AddReplacer(rule string, replacer ReplacerFunc)

AddReplacer registers how one rule fills the placeholders of its own message.

func (*Validator) AddReplacers

func (v *Validator) AddReplacers(replacers map[string]ReplacerFunc)

AddReplacers registers several replacers at once.

func (*Validator) AddRules

func (v *Validator) AddRules(rules *Set) *Validator

AddRules merges another compiled set into this validator's, so that a rule decided at request time joins the ones decided at boot.

What is merged is a Set, already parsed and checked by MustCompile. A field both sets declare keeps the rules of both, in the order this one wrote them first.

func (*Validator) After

func (v *Validator) After(callback func(*Validator)) *Validator

After registers a callback that runs once every rule has.

It is where a check that needs the whole form goes, and it is the seam a rule object is run through: the callback holds the Validator, so it calls AddFailure with the attribute and rule name of its choosing.

func (*Validator) AllFailingRequired

func (v *Validator) AllFailingRequired(attributes []string) bool

AllFailingRequired reports whether ALL of the attributes hold no answer.

func (*Validator) AnyFailingRequired

func (v *Validator) AnyFailingRequired(attributes []string) bool

AnyFailingRequired reports whether ANY of the attributes holds no answer.

func (*Validator) Attributes

func (v *Validator) Attributes() Data

Attributes returns the data the rules run against.

func (*Validator) Compare

func (v *Validator) Compare(result, against int, operator string) bool

Compare applies the operator to the result of a comparison. It takes what comparing the two values said rather than the values themselves, because there is no operator to apply to an any.

func (*Validator) CompareDates

func (v *Validator) CompareDates(attribute string, value any, parameters []string, operator string) bool

CompareDates reads both moments and answers what the operator says. A moment that cannot be read fails on either side -- a date rule that cannot read the date has not been passed.

The other moment is a field this rule set declares, one of the three keywords today, tomorrow and yesterday, or a literal date. The keywords are those three and nothing else: a rule whose accepted set nobody can enumerate cannot be reasoned about.

func (*Validator) Context

func (v *Validator) Context() context.Context

Context is the context every rule that leaves the process runs under: the request's, so a DNS lookup or a count query carries the request's deadline.

func (*Validator) CustomAttributes

func (v *Validator) CustomAttributes() map[string]string

CustomAttributes returns the field names in force, read for the reason CustomMessages is.

func (*Validator) CustomMessages

func (v *Validator) CustomMessages() map[string]any

CustomMessages returns the inline messages in force, which a rule object reads to build a sibling validator that says the same things.

func (*Validator) EnsureExponentWithinAllowedRange

func (v *Validator) EnsureExponentWithinAllowedRange(scale int, attribute string, value any) bool

EnsureExponentWithinAllowedRange asks that check.

With nothing registered the answer is the default range -- an exponent between -1000 and 1000 -- and never a plain yes: a range check that accepts every scale is no range check.

func (*Validator) EnsureExponentWithinAllowedRangeUsing

func (v *Validator) EnsureExponentWithinAllowedRangeUsing(callback func(scale int, attribute string, value any) bool) *Validator

EnsureExponentWithinAllowedRangeUsing sets the check `numeric` and `decimal` make before they read a number written in exponent notation, so that "1e100000" is refused rather than turned into a float nobody meant.

func (*Validator) Errors

func (v *Validator) Errors() Errors

Errors returns the messages a run collected, running first if it has not been.

func (*Validator) ExcludeAttribute

func (v *Validator) ExcludeAttribute(attribute string)

ExcludeAttribute drops the attribute from the validated data.

func (*Validator) ExtractDistinctValues

func (v *Validator) ExtractDistinctValues(attribute string) []any

ExtractDistinctValues reads the attribute and returns it as a list, or nothing when it holds no list.

func (*Validator) Failed

func (v *Validator) Failed() map[string][]string

Failed returns the rules that failed, per attribute.

func (*Validator) Fails

func (v *Validator) Fails() bool

Fails runs every rule and reports whether anything failed.

func (*Validator) FailsBasicDimensionChecks

func (v *Validator) FailsBasicDimensionChecks(parameters map[string]string, width, height int) bool

FailsBasicDimensionChecks reports whether the image misses any of width, min_width, max_width, height, min_height and max_height.

func (*Validator) FailsMaxRatioCheck

func (v *Validator) FailsMaxRatioCheck(parameters map[string]string, width, height int) bool

FailsMaxRatioCheck reports whether the image is taller than max_ratio allows.

func (*Validator) FailsMinRatioCheck

func (v *Validator) FailsMinRatioCheck(parameters map[string]string, width, height int) bool

FailsMinRatioCheck reports whether the image is wider than min_ratio allows.

func (*Validator) FailsRatioCheck

func (v *Validator) FailsRatioCheck(parameters map[string]string, width, height int) bool

FailsRatioCheck reports whether the image misses the ratio. The tolerance widens with the image, so that a 3/2 photograph of 1201 by 800 is still 3/2.

func (*Validator) GetDNSRecords

func (v *Validator) GetDNSRecords(hostname string) ([]string, error)

GetDNSRecords returns the addresses the hostname resolves to.

func (*Validator) GetData

func (v *Validator) GetData() Data

GetData returns the request the rules run against.

func (*Validator) GetDateFormat

func (v *Validator) GetDateFormat(attribute string) string

GetDateFormat returns the layout the field's date_format declares, and empty when it declares none.

func (*Validator) GetDisplayableAttribute

func (v *Validator) GetDisplayableAttribute(attribute string) string

GetDisplayableAttribute returns the name of a field as a sentence names it.

The default is the snake-cased name with its underscores turned into spaces: "password_confirmation" reads "password confirmation", LOWERCASE. The name sits inside the sentence ("The password confirmation field must match") and never at the head of one, so it is never capitalised here.

func (*Validator) GetDisplayableValue

func (v *Validator) GetDisplayableValue(attribute string, value any) string

GetDisplayableValue returns how a value is spelled inside a message, after the custom values an application declared and the validation.values lines.

func (*Validator) GetDistinctValues

func (v *Validator) GetDistinctValues(attribute string) []any

GetDistinctValues returns the values `distinct` compares: the list held at the attribute, for the reason ValidateDistinct gives.

func (*Validator) GetException

func (v *Validator) GetException() func(*Validator) error

GetException returns what a failure is turned into: the function that builds the error.

func (*Validator) GetExtraConditions

func (v *Validator) GetExtraConditions(segments []string) map[string]string

GetExtraConditions reads the trailing parameters in pairs, column then value.

func (*Validator) GetMessageBag

func (v *Validator) GetMessageBag() Errors

GetMessageBag returns the messages a run collected, which is what Errors returns.

func (*Validator) GetPresenceVerifier

func (v *Validator) GetPresenceVerifier() (PresenceVerifier, error)

GetPresenceVerifier returns the verifier `unique` and `exists` count through, and an error when none was set.

func (*Validator) GetQueryColumn

func (v *Validator) GetQueryColumn(parameters []string, attribute string) string

GetQueryColumn returns the column the rule queries: the second parameter when it names one, and the guess otherwise.

func (*Validator) GetRule

func (v *Validator) GetRule(attribute string, rules []string) (string, []string, bool)

GetRule returns the first of the given rules written against the attribute, and the parameters it carries. The third value reports whether one was found.

func (*Validator) GetRules

func (v *Validator) GetRules() map[string][]string

GetRules returns the rule names written against each field.

func (*Validator) GetRulesWithoutPlaceholders

func (v *Validator) GetRulesWithoutPlaceholders() map[string][]string

GetRulesWithoutPlaceholders returns the same map GetRules does, for the reason ParseData is only a copy: no key was ever escaped, so there is nothing to take back out.

func (*Validator) GetSize

func (v *Validator) GetSize(attribute string, value any) (*big.Rat, bool)

GetSize is what min, max, size, between and the four comparisons measure.

A number is its own size when the field declares numeric, integer or decimal; an array is how many members it has; a file is its KILOBYTES; anything else is how many characters it has. Characters, never bytes: a limit in bytes rejects valid input in every language that needs more than one byte for a letter.

The size is a *big.Rat and not a float64, because 9007199254740993 and 9007199254740992 are the SAME float64: "numeric|max:9007199254740992" would pass on the value 9007199254740993, and a monetary limit or a quota would be over-runnable by one unit of rounding. math/big is stdlib and the comparison is exact.

The bool is false for an exponent outside the allowed range, which gives a value no size at all. Every caller fails the rule on it, which is the closed answer.

func (*Validator) GetTranslator

func (v *Validator) GetTranslator() Translator

GetTranslator returns the catalogue messages are read out of, which is never nil: without one it is a translator that finds no line.

func (*Validator) GetUniqueExtra

func (v *Validator) GetUniqueExtra(parameters []string) map[string]string

GetUniqueExtra returns the extra conditions of a `unique` rule, which are its parameters past the fourth.

func (*Validator) GetUniqueIds

func (v *Validator) GetUniqueIds(idColumn string, parameters []string) (string, any)

GetUniqueIds returns the column and the value of the row the rule must ignore, which is the row being edited.

func (*Validator) GetValue

func (v *Validator) GetValue(attribute string) any

GetValue returns what the request holds at the attribute.

func (*Validator) GuessColumnForQuery

func (v *Validator) GuessColumnForQuery(attribute string) string

GuessColumnForQuery returns the last segment of a dotted attribute, and the attribute itself otherwise.

func (*Validator) HasRule

func (v *Validator) HasRule(attribute string, rules []string) bool

HasRule reports whether the field declares any of the named rules.

func (*Validator) Invalid

func (v *Validator) Invalid() Data

Invalid returns the data of every attribute that has a message.

func (*Validator) IsValidFileInstance

func (v *Validator) IsValidFileInstance(value any) bool

IsValidFileInstance reports whether the value is a File, and an upload that finished.

func (*Validator) MakeReplacements

func (v *Validator) MakeReplacements(message, attribute, rule string, parameters []string) string

MakeReplacements fills every placeholder of a message with what actually happened.

It is exported because a rule object reaches for it.

func (*Validator) Messages

func (v *Validator) Messages() Errors

Messages returns the same messages Errors does.

func (*Validator) Now

func (v *Validator) Now() time.Time

Now is the clock the relative date keywords read. It is settable so that a test of `after:today` does not have to be run before midnight.

func (*Validator) ParseData

func (v *Validator) ParseData(data Data) Data

ParseData returns the request as the Validator will read it, which is a copy.

Nothing has to be escaped first: lookup tries the literal key before it walks a dotted path, so an input genuinely named "a.b" is found as itself. What is left is the copy, which keeps a Validator from writing through to the request's own map.

func (*Validator) ParseDependentRuleParameters

func (v *Validator) ParseDependentRuleParameters(parameters []string) ([]string, any)

ParseDependentRuleParameters returns the values a dependent rule compares against, and the other attribute's value.

Nothing is converted here. Reading "true", "false" and "null" as what they name is the comparison's job, so that one place decides what "the other field says yes" means.

func (*Validator) ParseNamedParameters

func (v *Validator) ParseNamedParameters(parameters []string) map[string]string

ParseNamedParameters reads parameters of the form "min_width=100" into a map.

func (*Validator) ParseTable

func (v *Validator) ParseTable(table string) (connection, name, idColumn string)

ParseTable splits "connection.table", and reads a bare name as the table. A rule names a table, never a model: there are no models.

func (*Validator) Passes

func (v *Validator) Passes() bool

Passes runs every rule and reports whether nothing failed.

func (*Validator) PrepareUniqueId

func (v *Validator) PrepareUniqueId(id string) any

PrepareUniqueId reads the ignored row's id: "[field]" takes it out of the submitted data, "null" is null, and a whole number is a number.

func (*Validator) ReplaceProhibitedIfDeclined

func (v *Validator) ReplaceProhibitedIfDeclined(message, attribute, rule string, parameters []string) string

ReplaceProhibitedIfDeclined fills :other for `prohibited_if_declined`, exported for the reason ReplaceRequiredIfDeclined is.

func (*Validator) ReplaceRequiredIfDeclined

func (v *Validator) ReplaceRequiredIfDeclined(message, attribute, rule string, parameters []string) string

ReplaceRequiredIfDeclined fills :other for `required_if_declined`. It is exported because it is called from outside this package.

func (*Validator) RequireParameterCount

func (v *Validator) RequireParameterCount(count int, parameters []string, rule string) bool

RequireParameterCount reports whether the rule was given the parameters it needs, and the rule that asked returns false when it was not.

The count is already proven at boot by compile.go, so this is the second lock on a door the first one closed -- and a rule invoked directly, outside a compiled set, still cannot read past the end of its parameters.

func (*Validator) Safe

func (v *Validator) Safe(keys ...string) (*support.ValidatedInput, error)

Safe returns the validated attributes in a container that reads them one at a time, narrowed to the named keys when any are given.

It is a ValidatedInput either way, so a caller has one type to write against, and ValidatedInput.All answers with the map.

func (*Validator) SetAttributeNames

func (v *Validator) SetAttributeNames(attributes map[string]string) *Validator

SetAttributeNames replaces the field names in force.

func (*Validator) SetCustomMessages

func (v *Validator) SetCustomMessages(messages map[string]any) *Validator

SetCustomMessages merges more inline messages into the ones in force.

func (*Validator) SetData

func (v *Validator) SetData(data Data) *Validator

SetData replaces the request the rules run against. The data is copied, for the reason Make copies it.

func (*Validator) SetException

func (v *Validator) SetException(exception func(*Validator) error) *Validator

SetException sets what a failure is turned into. The signature settles the shape, so only a nil factory is left to reject.

func (*Validator) SetFallbackMessages

func (v *Validator) SetFallbackMessages(messages map[string]any)

SetFallbackMessages sets the sentence a rule registered with Factory.Extend says when nothing else names one.

func (*Validator) SetImplicitAttributesFormatter

func (v *Validator) SetImplicitAttributesFormatter(formatter func(string) string) *Validator

SetImplicitAttributesFormatter sets how a wildcard attribute that nothing named is spelled.

func (*Validator) SetPresenceVerifier

func (v *Validator) SetPresenceVerifier(g auth.Grant, presenceVerifier PresenceVerifier)

SetPresenceVerifier gives `unique` and `exists` the Grant and the verifier they need.

There is no way to give a verifier without a Grant: a read is authorized like any other. WithPresence is the same pair as an option.

func (*Validator) SetRules

func (v *Validator) SetRules(rules *Set) *Validator

SetRules replaces the compiled set this runs.

func (*Validator) SetTranslator

func (v *Validator) SetTranslator(translator Translator)

SetTranslator sets the catalogue messages are read out of.

func (*Validator) SetValue

func (v *Validator) SetValue(attribute string, value any)

SetValue writes a value at the attribute.

func (*Validator) SetValueNames

func (v *Validator) SetValueNames(values map[string]map[string]string) *Validator

SetValueNames replaces the value names in force.

func (*Validator) ShouldBeExcluded

func (v *Validator) ShouldBeExcluded(attribute string) bool

ShouldBeExcluded reports whether the attribute, or a parent of it, was excluded.

func (*Validator) ShouldBlockPhpUpload

func (v *Validator) ShouldBlockPhpUpload(value any, parameters []string) bool

ShouldBlockPhpUpload reports whether the explicit `extensions` rule refuses the client-announced name: one of phpExtensions is, unless the rule asked for php by name. Content-based rules deliberately do not consult this metadata.

func (*Validator) ShouldStopValidating

func (v *Validator) ShouldStopValidating(attribute string) bool

ShouldStopValidating reports whether the field is finished, whatever rules it has left.

`bail` stops the field at its first failure. A failed implicit rule stops it too, without being asked: `required` failing must not also produce "must be at least 12 characters" about the same empty box.

func (*Validator) Sometimes

func (v *Validator) Sometimes(attributes []string, rules *Set, callback func(payload *support.Fluent, value any) bool) *Validator

Sometimes adds rules to an attribute only when the callback says so.

The callback is handed the whole request, as a Fluent, and the value of the attribute being decided.

func (*Validator) StopOnFirstFailure

func (v *Validator) StopOnFirstFailure() *Validator

StopOnFirstFailure leaves after the first field that fails, rather than reporting every field. It returns the Validator, so calls chain.

func (*Validator) Valid

func (v *Validator) Valid() Data

Valid returns the data of every attribute that has no message.

func (*Validator) Validate

func (v *Validator) Validate() (Input, error)

Validate returns the attributes that were validated, or the error the failures make.

The error is always a *ValidationException, so errors.As reaches the bag, the status and the redirect.

func (*Validator) ValidateAccepted

func (v *Validator) ValidateAccepted(attribute string, value any, parameters []string) bool

ValidateAccepted is `accepted`: the value is one of yes, on, 1 or true. It implies the attribute is required.

func (*Validator) ValidateAcceptedIf

func (v *Validator) ValidateAcceptedIf(attribute string, value any, parameters []string) bool

ValidateAcceptedIf is `accepted_if`: accepted when the other attribute holds one of the given values.

func (*Validator) ValidateActiveUrl

func (v *Validator) ValidateActiveUrl(attribute string, value any, parameters []string) bool

ValidateActiveUrl is `active_url`: the host of the value has an A or an AAAA record.

The name spells Url rather than URL because `url` is the rule somebody types, and ValidateUrl would then read differently from its neighbour here.

This puts a DNS lookup on the request path. The deadline comes from the Validator's context, which is the request's.

func (*Validator) ValidateAfter

func (v *Validator) ValidateAfter(attribute string, value any, parameters []string) bool

ValidateAfter is `after`: the date is later than the other moment.

func (*Validator) ValidateAfterOrEqual

func (v *Validator) ValidateAfterOrEqual(attribute string, value any, parameters []string) bool

ValidateAfterOrEqual is `after_or_equal`: the date is not earlier than the other moment.

func (*Validator) ValidateAlpha

func (v *Validator) ValidateAlpha(attribute string, value any, parameters []string) bool

ValidateAlpha is `alpha`: letters only. With the parameter "ascii" it is the ASCII letters only.

func (*Validator) ValidateAlphaDash

func (v *Validator) ValidateAlphaDash(attribute string, value any, parameters []string) bool

ValidateAlphaDash is `alpha_dash`: letters, digits, dashes and underscores. With the parameter "ascii" it is the ASCII ones only.

func (*Validator) ValidateAlphaNum

func (v *Validator) ValidateAlphaNum(attribute string, value any, parameters []string) bool

ValidateAlphaNum is `alpha_num`: letters and digits. With the parameter "ascii" it is the ASCII ones only.

func (*Validator) ValidateArray

func (v *Validator) ValidateArray(attribute string, value any, parameters []string) bool

ValidateArray is `array`: the value is an array, and with parameters, an array with no key outside them.

func (*Validator) ValidateAscii

func (v *Validator) ValidateAscii(attribute string, value any, parameters []string) bool

ValidateAscii is `ascii`: every character is a 7-bit ASCII one.

func (*Validator) ValidateBail

func (v *Validator) ValidateBail(attribute string, value any, parameters []string) bool

ValidateBail is `bail`. It is a marker: Set.Validate reads it and stops the field at its first failure.

func (*Validator) ValidateBefore

func (v *Validator) ValidateBefore(attribute string, value any, parameters []string) bool

ValidateBefore is `before`: the date is earlier than the other moment.

func (*Validator) ValidateBeforeOrEqual

func (v *Validator) ValidateBeforeOrEqual(attribute string, value any, parameters []string) bool

ValidateBeforeOrEqual is `before_or_equal`: the date is not later than the other moment.

func (*Validator) ValidateBetween

func (v *Validator) ValidateBetween(attribute string, value any, parameters []string) bool

ValidateBetween is `between`: the size is within the two bounds, both of them inclusive.

func (*Validator) ValidateBoolean

func (v *Validator) ValidateBoolean(attribute string, value any, parameters []string) bool

ValidateBoolean is `boolean`. The comparison is by type as well as by value, so exactly six pass: true, false, 0, 1, "0" and "1". A ticked checkbox sends "on", which is what `accepted` is for.

func (*Validator) ValidateConfirmed

func (v *Validator) ValidateConfirmed(attribute string, value any, parameters []string) bool

ValidateConfirmed is `confirmed`: the value is repeated by <attribute>_confirmation, or by the attribute the parameter names.

func (*Validator) ValidateContains

func (v *Validator) ValidateContains(attribute string, value any, parameters []string) bool

ValidateContains is `contains`: the array holds every one of the given values.

func (*Validator) ValidateCurrentPassword

func (v *Validator) ValidateCurrentPassword(attribute string, value any, parameters []string) bool

ValidateCurrentPassword is `current_password`: the value is the password of whoever is signed in.

The question is asked of the CurrentPasswordChecker given to the Validator with WithCurrentPassword. Without one the rule fails closed, because a password check that passes for want of a checker is the worst outcome available. The parameter, when given, names the guard.

func (*Validator) ValidateDate

func (v *Validator) ValidateDate(attribute string, value any, parameters []string) bool

ValidateDate is `date`: the value reads as a date.

func (*Validator) ValidateDateEquals

func (v *Validator) ValidateDateEquals(attribute string, value any, parameters []string) bool

ValidateDateEquals is `date_equals`: the date is the other moment.

func (*Validator) ValidateDateFormat

func (v *Validator) ValidateDateFormat(attribute string, value any, parameters []string) bool

ValidateDateFormat is `date_format`: the value is a date written in one of the given layouts.

The layout is a GO layout -- date_format:2006-01-02, never date_format:Y-m-d. compile.go refuses a layout it cannot read at boot, rather than accepting one that then rejects every date.

More than one layout may be given, and the value passes when ANY of them matches. A numeric value is read as the text it prints as, so a JSON body that sent 20240301 unquoted is not refused for having no quotes.

func (*Validator) ValidateDecimal

func (v *Validator) ValidateDecimal(attribute string, value any, parameters []string) bool

ValidateDecimal is `decimal`: the count of digits after the point is the given number, or between the two given numbers.

func (*Validator) ValidateDeclined

func (v *Validator) ValidateDeclined(attribute string, value any, parameters []string) bool

ValidateDeclined is `declined`: the value is one of no, off, 0 or false. It implies the attribute is required.

func (*Validator) ValidateDeclinedIf

func (v *Validator) ValidateDeclinedIf(attribute string, value any, parameters []string) bool

ValidateDeclinedIf is `declined_if`: declined when the other attribute holds one of the given values.

func (*Validator) ValidateDifferent

func (v *Validator) ValidateDifferent(attribute string, value any, parameters []string) bool

ValidateDifferent is `different`: this attribute holds none of the values the named ones hold. An attribute the form did not send is not compared: a field that is not in the form is not the same as this one.

func (*Validator) ValidateDigits

func (v *Validator) ValidateDigits(attribute string, value any, parameters []string) bool

ValidateDigits is `digits`: the value is digits only, and exactly the given number of them.

func (*Validator) ValidateDigitsBetween

func (v *Validator) ValidateDigitsBetween(attribute string, value any, parameters []string) bool

ValidateDigitsBetween is `digits_between`: the value is digits only, and how many of them is within the two bounds.

func (*Validator) ValidateDimensions

func (v *Validator) ValidateDimensions(attribute string, value any, parameters []string) bool

ValidateDimensions is `dimensions`: the image measures what the named parameters ask for.

An SVG passes without being measured once its document is validated: it has no pixels to count. Validating it means reading its bytes, so a file that announces an SVG it will not let anyone read fails instead of passing. Supported raster bytes are decoded under fixed input and pixel bounds before their dimensions are compared.

func (*Validator) ValidateDistinct

func (v *Validator) ValidateDistinct(attribute string, value any, parameters []string) bool

ValidateDistinct is `distinct`: no value repeats.

There are no wildcards in a rule string, so the rule is asked of the whole list at once rather than once per member. The parameters are `ignore_case`, which compares case-insensitively, and `strict`, which compares types as well as values.

func (*Validator) ValidateDoesntEndWith

func (v *Validator) ValidateDoesntEndWith(attribute string, value any, parameters []string) bool

ValidateDoesntEndWith is `doesnt_end_with`: the value ends with none of the given suffixes.

func (*Validator) ValidateDoesntStartWith

func (v *Validator) ValidateDoesntStartWith(attribute string, value any, parameters []string) bool

ValidateDoesntStartWith is `doesnt_start_with`: the value begins with none of the given prefixes.

func (*Validator) ValidateEmail

func (v *Validator) ValidateEmail(attribute string, value any, parameters []string) bool

ValidateEmail is `email`: the value has the shape of an address.

Shape is all it checks by default. Deliverability is proven by sending mail; the parameter `dns` asks whether the domain resolves at all, which is as far as a lookup can go.

func (*Validator) ValidateEndsWith

func (v *Validator) ValidateEndsWith(attribute string, value any, parameters []string) bool

ValidateEndsWith is `ends_with`: the value ends with one of the given suffixes.

func (*Validator) ValidateEnum

func (v *Validator) ValidateEnum(attribute string, value any, parameters []string) bool

ValidateEnum is `enum`: the value is one of the cases.

A value that carries its own type -- a generated enum -- is asked, and the parameters are not consulted for membership. The list in a rule string is a second copy of a set the type already holds, and a second copy can disagree with it: a case added to the type and not to the rule used to reject a value the column accepts, and the two were never compared by anything.

The parameters are still read, for exactly that disagreement. A case named in the rule that the type does not declare fails the rule outright, whatever the value was, because a rule listing a case nothing can produce was written against a different type -- and passing the field would hide it until the case that is missing arrives.

Written against an untyped value -- a string straight off a form, which is what a field that has not been typed yet carries -- the parameters are the only set there is, and the rule reads them. Without either, there is nothing to decide against and the value is refused rather than waved through.

func (*Validator) ValidateExclude

func (v *Validator) ValidateExclude(attribute string, value any, parameters []string) bool

ValidateExclude is `exclude`: always excluded.

func (*Validator) ValidateExcludeIf

func (v *Validator) ValidateExcludeIf(attribute string, value any, parameters []string) bool

ValidateExcludeIf is `exclude_if`: excluded when the other attribute holds one of the given values.

func (*Validator) ValidateExcludeUnless

func (v *Validator) ValidateExcludeUnless(attribute string, value any, parameters []string) bool

ValidateExcludeUnless is `exclude_unless`: excluded unless the other attribute holds one of the given values.

func (*Validator) ValidateExcludeWith

func (v *Validator) ValidateExcludeWith(attribute string, value any, parameters []string) bool

ValidateExcludeWith is `exclude_with`: excluded when the named attribute was sent.

func (*Validator) ValidateExcludeWithout

func (v *Validator) ValidateExcludeWithout(attribute string, value any, parameters []string) bool

ValidateExcludeWithout is `exclude_without`: excluded when the named attributes hold no answer.

func (*Validator) ValidateExists

func (v *Validator) ValidateExists(attribute string, value any, parameters []string) bool

ValidateExists is `exists`: the table holds a row with this value.

The verifier arrives with the request, through WithPresence, together with the Grant -- because a rule that reads a table to answer "does this exist" is a read, and a read is authorized like any other. Without a verifier the rule fails closed.

func (*Validator) ValidateExtensions

func (v *Validator) ValidateExtensions(attribute string, value any, parameters []string) bool

ValidateExtensions is `extensions`: the extension THE CLIENT SENT is one of the given ones. It is the counterpart of mimes, which asks the content instead.

func (*Validator) ValidateFile

func (v *Validator) ValidateFile(attribute string, value any, parameters []string) bool

ValidateFile is `file`: the value is an upload that finished.

func (*Validator) ValidateFilled

func (v *Validator) ValidateFilled(attribute string, value any, parameters []string) bool

ValidateFilled is `filled`: a key that was sent holds an answer. A key that was not sent passes, which is the whole difference from required.

func (*Validator) ValidateGt

func (v *Validator) ValidateGt(attribute string, value any, parameters []string) bool

ValidateGt is `gt`: greater than a literal bound, or than another attribute.

func (*Validator) ValidateGte

func (v *Validator) ValidateGte(attribute string, value any, parameters []string) bool

ValidateGte is `gte`: greater than or equal to a literal bound, or to another attribute.

func (*Validator) ValidateHexColor

func (v *Validator) ValidateHexColor(attribute string, value any, parameters []string) bool

ValidateHexColor is `hex_color`: the value is a hexadecimal colour.

func (*Validator) ValidateImage

func (v *Validator) ValidateImage(attribute string, value any, parameters []string) bool

ValidateImage is `image`: a complete PNG, JPEG or GIF decoded under fixed bounds, plus a well-formed SVG when the parameters carry allow_svg. Formats without an audited decoder fail closed even when their MIME can be detected.

func (*Validator) ValidateIn

func (v *Validator) ValidateIn(attribute string, value any, parameters []string) bool

ValidateIn is `in`: the value is one of the parameters.

A value that is an array on a field that also declares `array` passes when every member is one of the parameters, which is how a multi-select is validated in one rule.

func (*Validator) ValidateInArray

func (v *Validator) ValidateInArray(attribute string, value any, parameters []string) bool

ValidateInArray is `in_array`: the value is one of the values held by the other attribute.

func (*Validator) ValidateInteger

func (v *Validator) ValidateInteger(attribute string, value any, parameters []string) bool

ValidateInteger is `integer`: the value reads as a whole number.

func (*Validator) ValidateIp

func (v *Validator) ValidateIp(attribute string, value any, parameters []string) bool

ValidateIp is `ip`: the value is an IP address of either version.

func (*Validator) ValidateIpv4

func (v *Validator) ValidateIpv4(attribute string, value any, parameters []string) bool

ValidateIpv4 is `ipv4`: the value is an IPv4 address.

func (*Validator) ValidateIpv6

func (v *Validator) ValidateIpv6(attribute string, value any, parameters []string) bool

ValidateIpv6 is `ipv6`: the value is an IPv6 address.

func (*Validator) ValidateJson

func (v *Validator) ValidateJson(attribute string, value any, parameters []string) bool

ValidateJson is `json`: the value is text that parses as JSON.

func (*Validator) ValidateList

func (v *Validator) ValidateList(attribute string, value any, parameters []string) bool

ValidateList is `list`: the value is an array with consecutive integer keys. A Data is an array with names, so it is not a list; a []any is.

func (*Validator) ValidateLowercase

func (v *Validator) ValidateLowercase(attribute string, value any, parameters []string) bool

ValidateLowercase is `lowercase`: the value is already all lower case.

func (*Validator) ValidateLt

func (v *Validator) ValidateLt(attribute string, value any, parameters []string) bool

ValidateLt is `lt`: less than a literal bound, or than another attribute.

func (*Validator) ValidateLte

func (v *Validator) ValidateLte(attribute string, value any, parameters []string) bool

ValidateLte is `lte`: less than or equal to a literal bound, or to another attribute.

func (*Validator) ValidateMacAddress

func (v *Validator) ValidateMacAddress(attribute string, value any, parameters []string) bool

ValidateMacAddress is `mac_address`: the value is a MAC address.

func (*Validator) ValidateMax

func (v *Validator) ValidateMax(attribute string, value any, parameters []string) bool

ValidateMax is `max`: the size is at or below the bound. An upload that did not finish fails before its size is asked for.

func (*Validator) ValidateMaxDigits

func (v *Validator) ValidateMaxDigits(attribute string, value any, parameters []string) bool

ValidateMaxDigits is `max_digits`: the value is digits only, and at most the given number of them.

func (*Validator) ValidateMimes

func (v *Validator) ValidateMimes(attribute string, value any, parameters []string) bool

ValidateMimes is `mimes`: the extension GUESSED FROM THE CONTENT is one of the given ones, which is why a .png renamed to .jpg does not pass. jpg and jpeg each stand for the other.

func (*Validator) ValidateMimetypes

func (v *Validator) ValidateMimetypes(attribute string, value any, parameters []string) bool

ValidateMimetypes is `mimetypes`: the media type of the content is one of the given ones. A parameter of the form "image/*" matches every type in the group.

func (*Validator) ValidateMin

func (v *Validator) ValidateMin(attribute string, value any, parameters []string) bool

ValidateMin is `min`: the size is at or above the bound.

func (*Validator) ValidateMinDigits

func (v *Validator) ValidateMinDigits(attribute string, value any, parameters []string) bool

ValidateMinDigits is `min_digits`: the value is digits only, and at least the given number of them.

func (*Validator) ValidateMissing

func (v *Validator) ValidateMissing(attribute string, value any, parameters []string) bool

ValidateMissing is `missing`: the key was not sent at all.

func (*Validator) ValidateMissingIf

func (v *Validator) ValidateMissingIf(attribute string, value any, parameters []string) bool

ValidateMissingIf is `missing_if`: the key was not sent when the other attribute holds one of the given values.

func (*Validator) ValidateMissingUnless

func (v *Validator) ValidateMissingUnless(attribute string, value any, parameters []string) bool

ValidateMissingUnless is `missing_unless`: the key was not sent unless the other attribute holds one of the given values.

func (*Validator) ValidateMissingWith

func (v *Validator) ValidateMissingWith(attribute string, value any, parameters []string) bool

ValidateMissingWith is `missing_with`: the key was not sent when ANY of the named attributes was.

func (*Validator) ValidateMissingWithAll

func (v *Validator) ValidateMissingWithAll(attribute string, value any, parameters []string) bool

ValidateMissingWithAll is `missing_with_all`: the key was not sent when ALL of the named attributes were.

func (*Validator) ValidateMultipleOf

func (v *Validator) ValidateMultipleOf(attribute string, value any, parameters []string) bool

ValidateMultipleOf is `multiple_of`: the value divides by the parameter with no remainder.

Zero on both sides fails, a zero numerator over a non-zero denominator passes, and a zero denominator fails -- three cases written out, because "is 0 a multiple of 0" has no answer worth guessing.

func (*Validator) ValidateNotIn

func (v *Validator) ValidateNotIn(attribute string, value any, parameters []string) bool

ValidateNotIn is `not_in`: the value is none of the parameters.

func (*Validator) ValidateNotRegex

func (v *Validator) ValidateNotRegex(attribute string, value any, parameters []string) bool

ValidateNotRegex is `not_regex`: the value does not match the pattern, which is a GO pattern for the reason ValidateRegex gives.

func (*Validator) ValidateNullable

func (v *Validator) ValidateNullable(attribute string, value any, parameters []string) bool

ValidateNullable is `nullable`. It is a marker: Set.Validate reads it and stops the field when the value is null -- null, not empty, which is the whole of the difference from leaving `required` off.

func (*Validator) ValidateNumeric

func (v *Validator) ValidateNumeric(attribute string, value any, parameters []string) bool

ValidateNumeric is `numeric`: the value reads as a number.

func (*Validator) ValidatePresent

func (v *Validator) ValidatePresent(attribute string, value any, parameters []string) bool

ValidatePresent is `present`: the key was sent, whatever it holds.

func (*Validator) ValidatePresentIf

func (v *Validator) ValidatePresentIf(attribute string, value any, parameters []string) bool

ValidatePresentIf is `present_if`: the key was sent when the other attribute holds one of the given values.

func (*Validator) ValidatePresentUnless

func (v *Validator) ValidatePresentUnless(attribute string, value any, parameters []string) bool

ValidatePresentUnless is `present_unless`: the key was sent unless the other attribute holds one of the given values.

func (*Validator) ValidatePresentWith

func (v *Validator) ValidatePresentWith(attribute string, value any, parameters []string) bool

ValidatePresentWith is `present_with`: the key was sent when ANY of the named attributes was.

func (*Validator) ValidatePresentWithAll

func (v *Validator) ValidatePresentWithAll(attribute string, value any, parameters []string) bool

ValidatePresentWithAll is `present_with_all`: the key was sent when ALL of the named attributes were.

func (*Validator) ValidateProhibited

func (v *Validator) ValidateProhibited(attribute string, value any, parameters []string) bool

ValidateProhibited is `prohibited`: the attribute holds no answer.

func (*Validator) ValidateProhibitedIf

func (v *Validator) ValidateProhibitedIf(attribute string, value any, parameters []string) bool

ValidateProhibitedIf is `prohibited_if`: prohibited when the other attribute holds one of the given values.

func (*Validator) ValidateProhibitedIfAccepted

func (v *Validator) ValidateProhibitedIfAccepted(attribute string, value any, parameters []string) bool

ValidateProhibitedIfAccepted is `prohibited_if_accepted`: prohibited when the other attribute is accepted.

func (*Validator) ValidateProhibitedIfDeclined

func (v *Validator) ValidateProhibitedIfDeclined(attribute string, value any, parameters []string) bool

ValidateProhibitedIfDeclined is `prohibited_if_declined`: prohibited when the other attribute is declined.

func (*Validator) ValidateProhibitedUnless

func (v *Validator) ValidateProhibitedUnless(attribute string, value any, parameters []string) bool

ValidateProhibitedUnless is `prohibited_unless`: prohibited unless the other attribute holds one of the given values.

func (*Validator) ValidateProhibits

func (v *Validator) ValidateProhibits(attribute string, value any, parameters []string) bool

ValidateProhibits is `prohibits`: this attribute being present forbids the named ones.

func (*Validator) ValidateRegex

func (v *Validator) ValidateRegex(attribute string, value any, parameters []string) bool

ValidateRegex is `regex`: the value matches the pattern.

The pattern is a GO pattern: RE2, with no backreference and no lookaround, and with no delimiters around it. compile.go compiles it at boot, so an unclosed group is a boot failure naming the field rather than a 500 on the first form somebody submits.

func (*Validator) ValidateRequired

func (v *Validator) ValidateRequired(attribute string, value any, parameters []string) bool

ValidateRequired is `required`: the attribute holds an answer.

Absent is one of four things, and the four are the whole rule: null, a string that is nothing but whitespace, an array with no members, and a file that was never written. A form sending three spaces has not answered the question.

func (*Validator) ValidateRequiredArrayKeys

func (v *Validator) ValidateRequiredArrayKeys(attribute string, value any, parameters []string) bool

ValidateRequiredArrayKeys is `required_array_keys`: the value is a keyed array holding every one of the named keys.

func (*Validator) ValidateRequiredIf

func (v *Validator) ValidateRequiredIf(attribute string, value any, parameters []string) bool

ValidateRequiredIf is `required_if`: required when the other attribute holds one of the given values.

A key the form did not send at all passes: a form that does not carry the other field cannot be answering it.

func (*Validator) ValidateRequiredIfAccepted

func (v *Validator) ValidateRequiredIfAccepted(attribute string, value any, parameters []string) bool

ValidateRequiredIfAccepted is `required_if_accepted`: required when the other attribute is accepted.

func (*Validator) ValidateRequiredIfDeclined

func (v *Validator) ValidateRequiredIfDeclined(attribute string, value any, parameters []string) bool

ValidateRequiredIfDeclined is `required_if_declined`: required when the other attribute is declined.

func (*Validator) ValidateRequiredUnless

func (v *Validator) ValidateRequiredUnless(attribute string, value any, parameters []string) bool

ValidateRequiredUnless is `required_unless`: required unless the other attribute holds one of the given values.

func (*Validator) ValidateRequiredWith

func (v *Validator) ValidateRequiredWith(attribute string, value any, parameters []string) bool

ValidateRequiredWith is `required_with`: required when ANY of the named attributes is present.

func (*Validator) ValidateRequiredWithAll

func (v *Validator) ValidateRequiredWithAll(attribute string, value any, parameters []string) bool

ValidateRequiredWithAll is `required_with_all`: required when ALL of the named attributes are present.

func (*Validator) ValidateRequiredWithout

func (v *Validator) ValidateRequiredWithout(attribute string, value any, parameters []string) bool

ValidateRequiredWithout is `required_without`: required when ANY of the named attributes is missing.

func (*Validator) ValidateRequiredWithoutAll

func (v *Validator) ValidateRequiredWithoutAll(attribute string, value any, parameters []string) bool

ValidateRequiredWithoutAll is `required_without_all`: required when ALL of the named attributes are missing.

func (*Validator) ValidateSame

func (v *Validator) ValidateSame(attribute string, value any, parameters []string) bool

ValidateSame is `same`: this attribute and the named one hold the same value.

func (*Validator) ValidateSize

func (v *Validator) ValidateSize(attribute string, value any, parameters []string) bool

ValidateSize is `size`: the size is exactly the given one.

The size is four things under one name, as GetSize measures it: the characters of a string, the value of a number when the field declares numeric, integer or decimal, the members of an array, and the KILOBYTES of a file.

func (*Validator) ValidateSometimes

func (v *Validator) ValidateSometimes(attribute string, value any, parameters []string) bool

ValidateSometimes is `sometimes`. It is a marker: Set.Validate skips the field when its key was not sent at all.

func (*Validator) ValidateStartsWith

func (v *Validator) ValidateStartsWith(attribute string, value any, parameters []string) bool

ValidateStartsWith is `starts_with`: the value begins with one of the given prefixes.

func (*Validator) ValidateString

func (v *Validator) ValidateString(attribute string, value any, parameters []string) bool

ValidateString is `string`: the value is text.

It is not the tautology it looks like. A JSON body carries numbers, booleans and lists, and this is the rule that refuses them where text was asked for.

func (*Validator) ValidateTimezone

func (v *Validator) ValidateTimezone(attribute string, value any, parameters []string) bool

ValidateTimezone is `timezone`: the value names a zone the system knows.

func (*Validator) ValidateUlid

func (v *Validator) ValidateUlid(attribute string, value any, parameters []string) bool

ValidateUlid is `ulid`: the value is a ULID.

func (*Validator) ValidateUnique

func (v *Validator) ValidateUnique(attribute string, value any, parameters []string) bool

ValidateUnique is `unique`: the table holds no row with this value.

The parameters, in order: unique:table,column,exceptID,idColumn,extraColumn,extraValue...

func (*Validator) ValidateUppercase

func (v *Validator) ValidateUppercase(attribute string, value any, parameters []string) bool

ValidateUppercase is `uppercase`: the value is already all upper case.

func (*Validator) ValidateUrl

func (v *Validator) ValidateUrl(attribute string, value any, parameters []string) bool

ValidateUrl is `url`. The parameters are the schemes allowed, and http and https are the default.

func (*Validator) ValidateUsingCustomRule

func (v *Validator) ValidateUsingCustomRule(attribute string, value any, rule Rule)

ValidateUsingCustomRule runs one rule object and puts whatever it says on the attribute.

A compiled Set holds rule names and not objects, so the only caller is an After hook -- which is why this is exported.

func (*Validator) ValidateUuid

func (v *Validator) ValidateUuid(attribute string, value any, parameters []string) bool

ValidateUuid is `uuid`: the value is a UUID.

func (*Validator) ValidateWithBag

func (v *Validator) ValidateWithBag(errorBag string) (Input, error)

ValidateWithBag is Validate with the failures named, so that two forms on one page do not draw each other's errors.

func (*Validator) Validated

func (v *Validator) Validated() Input

Validated returns the values that were declared, sent and not rejected. It is the only way to read a submitted value out of a validated request, so a field nobody wrote a rule for cannot reach a repository through here.

type ValidatorAwareRule

type ValidatorAwareRule interface {
	// SetValidator hands the rule the validator running it.
	SetValidator(validator *Validator)
}

ValidatorAwareRule is implemented by a rule object that needs the validator running it -- to phrase its message through the same translator, to read the custom messages and attribute names in force, or to reach a value it was not handed. The validator passes itself in before asking whether the value passes, and the rule keeps it for the length of that run.

type ValidatorOption

type ValidatorOption func(*Validator)

ValidatorOption is what a request hands a Validator that a boot-time rule set cannot hold: the context it runs under, the Grant that authorizes its queries, and the collaborators of the rules that leave the process.

func WithClock

func WithClock(now func() time.Time) ValidatorOption

WithClock sets the clock the relative date keywords read, so that a test of `after:today` does not have to be run before midnight.

func WithContext

func WithContext(ctx context.Context) ValidatorOption

WithContext gives the Validator the request's context, so that a lookup or a count query carries the request's deadline. Without it the context is Background, and a rule that leaves the process has no deadline at all.

func WithCurrentPassword

func WithCurrentPassword(c CurrentPasswordChecker) ValidatorOption

WithCurrentPassword gives `current_password` its checker.

func WithCustomAttributes

func WithCustomAttributes(attributes map[string]string) ValidatorOption

WithCustomAttributes sets how a field is named inside a sentence, which is what the validation.attributes lines of a catalogue also carry.

func WithCustomMessages

func WithCustomMessages(messages map[string]any) ValidatorOption

WithCustomMessages sets the sentence one field and one rule produce, keyed "field.rule", "rule" or "field", with "*" allowed in the field.

A value is a sentence, or -- for a size rule -- a group of them keyed by "numeric", "file", "array" and "string".

func WithCustomValues

func WithCustomValues(values map[string]map[string]string) ValidatorOption

WithCustomValues sets how one value of one field is spelled inside a sentence, so that ":value" reads "credit card" and not "cc".

func WithPresence

func WithPresence(g auth.Grant, p PresenceVerifier) ValidatorOption

WithPresence gives `unique` and `exists` the Grant and the verifier they need.

The Grant is not optional and there is no way to pass a verifier without one: a read is authorized like any other, and "the validator only counts rows" is how a count of rows becomes a way to ask whether another tenant has a user with a given email.

func WithResolver

func WithResolver(r Resolver) ValidatorOption

WithResolver gives `active_url` and `email:dns` the resolver to ask. The default is net.DefaultResolver; a test gives its own and makes no network call at all.

func WithTranslator

func WithTranslator(t Translator) ValidatorOption

WithTranslator gives the Validator the catalogue its messages are read out of, which is what turns "is required" into "The name field is required.".

It is optional. Without one the compiled rule set's own sentences are used, and every other layer -- inline messages, custom attribute names, the :placeholders -- still applies.

Jump to

Keyboard shortcuts

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