Conditions¶
When on the struct¶
When of the configurator returns a configurator whose rules are applied only if the function returns true for the validated object:
package main
import (
"context"
"fmt"
"github.com/insei/valigo"
"github.com/insei/valigo/rules/num"
"github.com/insei/valigo/rules/str"
)
type Sender struct {
Type string
SMTPHost string
SMTPPort int
HTTPURL string
}
func main() {
v := valigo.New()
valigo.Configure[Sender](v, func(b valigo.Configurator[Sender], s *Sender) {
b.Field(&s.Type).As(str.Of).AnyOf("smtp", "http")
smtp := b.When(func(_ context.Context, s *Sender) bool { return s.Type == "smtp" })
smtp.Field(&s.SMTPHost).As(str.Of).Required()
smtp.Field(&s.SMTPPort).As(num.Of).AnyOfInterval(1, 65535)
http := b.When(func(_ context.Context, s *Sender) bool { return s.Type == "http" })
http.Field(&s.HTTPURL).As(str.Of).Required()
})
fmt.Println(v.Validate(context.Background(), &Sender{Type: "smtp"}))
fmt.Println(v.Validate(context.Background(), &Sender{Type: "http"}))
}
Output:
[Should be fulfilled (SMTPHost: ) Only interval[1 - 65535] is allowed (SMTPPort: 0)]
[Should be fulfilled (HTTPURL: )]
- The returned configurator has all methods of the original one, the conditions of chained
Whencalls are combined with AND:b.When(a).When(b). Whendoesn't change the configurator it is called on:bkeeps adding unconditional rules.- The context passed to
Validateis passed to the condition, e.g. to enable rules by the role of the user.
When on a field¶
When of a field configurator returns a configurator of the same field whose rules are applied only if the function returns true for the value of the field. The rules before When stay unconditional:
b.Field(&u.Website).As(str.Of).
Trim(). // always
When(func(_ context.Context, v string) bool { return v != "" }).
Regexp(urlRe) // only for a non-empty value
The value is typed: string for str.Of, the number type for num.Of and num.Ptr, uuid.UUID for uuid.Of, and shared.Elems[E] for slices, see Slices.
For an absent value the condition isn't called and the rules after it are skipped, including Required.
Conditional requiredness¶
Tag based validators have families of tags like required_if, required_with and required_without. In valigo they are When with a condition on the fields:
package main
import (
"context"
"fmt"
"github.com/insei/valigo"
"github.com/insei/valigo/rules/str"
"github.com/insei/valigo/shared"
)
type Customer struct {
Kind string
TaxID string
Phone string
Email string
Company string
Title string
}
func main() {
v := valigo.New()
valigo.Configure[Customer](v, func(b valigo.Configurator[Customer], c *Customer) {
// required_if=Kind company
b.When(func(_ context.Context, c *Customer) bool { return c.Kind == "company" }).Field(&c.TaxID).As(str.Of).Required()
// required_without=Email: a phone or an email
b.When(func(_ context.Context, c *Customer) bool { return c.Email == "" }).Field(&c.Phone).As(str.Of).Required()
// required_with=Company: the title of the person in the company
b.When(func(_ context.Context, c *Customer) bool { return c.Company != "" }).Field(&c.Title).As(str.Of).Required()
// excluded_unless=Kind company
b.When(func(_ context.Context, c *Customer) bool { return c.Kind != "company" }).Field(&c.Company).As(str.Of).
Custom(func(ctx context.Context, h *shared.FieldCustomHelper, company string) []shared.Error {
if company != "" {
return []shared.Error{h.ErrorT(ctx, company, "Only companies have a company")}
}
return nil
})
})
fmt.Println(v.Validate(context.Background(), &Customer{Kind: "company"}))
fmt.Println(v.Validate(context.Background(), &Customer{Kind: "person", Email: "a@b.cc", Company: "ACME"}))
}
Output:
[Should be fulfilled (TaxID: ) Should be fulfilled (Phone: )]
[Should be fulfilled (Title: ) Only companies have a company (Company: ACME)]
Conditions and custom rules¶
A condition on several fields can also be written as a struct custom rule, which reports errors itself. Prefer When: the rules after it are the regular ones, with their messages and translations.