Rules on other fields¶
A rule can compare the value with the value of another field of the same object. The other field is passed by its pointer to the model, like the field itself, so its type is checked by the compiler and a pointer that isn't a field of the model panics in Configure:
b.Field(&o.EndsAt).As(timex.Of).AfterField(&o.StartsAt)
b.Field(&o.Price).As(num.Of).MinField(&o.MinPrice).MaxField(&o.MaxPrice)
b.Field(&u.Confirmation).As(str.Of).EqualField(&u.Password)
b.Field(&u.Plan).As(eq.Of).EqualField(&u.CurrentPlan)
| Configurator | Rule | Fails when | Message |
|---|---|---|---|
| strings | EqualField(&m.X) |
the value isn't equal to X |
Should be equal to %s |
| numbers | MinField(&m.X) |
the value is less than X |
Cannot be less than %v |
| numbers | MaxField(&m.X) |
the value is greater than X |
Cannot be greater than %v |
| time | BeforeField(&m.X) |
the value isn't before X |
Should be before %s |
| time | AfterField(&m.X) |
the value isn't after X |
Should be after %s |
| comparable values | EqualField(&m.X) |
the value isn't equal to X |
Should be equal to %s |
- The other field has the type of the configurator:
stringforstr.Of, the number type fornum.Of,time.Timefortimex.Of. - The message has the location of the other field, by the location function of the validator:
Should be after StartsAt, orShould be after starts_atwith JSON paths. - The rule is skipped when the value or the value of the other field is absent, e.g. a field of a nil nested struct.
- In
Eachthe elements are compared with a field of the object:e.As(timex.Of).AfterField(&m.StartsAt).
Example¶
package main
import (
"context"
"fmt"
"time"
"github.com/insei/valigo"
"github.com/insei/valigo/rules/num"
"github.com/insei/valigo/rules/str"
"github.com/insei/valigo/rules/timex"
)
type Auction struct {
StartsAt time.Time
EndsAt time.Time
MinBid int
Bids []int
Password string
Repeat string
}
func main() {
v := valigo.New()
valigo.Configure[Auction](v, func(b valigo.Configurator[Auction], a *Auction) {
b.Field(&a.EndsAt).As(timex.Of).AfterField(&a.StartsAt)
b.Slice(&a.Bids).As(num.Slice).Each(func(bid *num.BaseConfigurator[int]) { bid.MinField(&a.MinBid) })
b.Field(&a.Repeat).As(str.Of).EqualField(&a.Password)
})
start := time.Date(2026, 5, 1, 0, 0, 0, 0, time.UTC)
auction := &Auction{
StartsAt: start,
EndsAt: start.Add(-time.Hour),
MinBid: 100,
Bids: []int{150, 90},
Password: "secret",
Repeat: "Secret",
}
for _, err := range v.Validate(context.Background(), auction) {
fmt.Println(err)
}
}
Output:
Should be after StartsAt (EndsAt: 2026-04-30 23:00:00 +0000 UTC)
Cannot be less than MinBid (Bids[1]: 90)
Should be equal to Password (Repeat: Secret)
For a comparison of several fields that doesn't fit one rule, use a struct custom rule.