Compare Rules
Learn how to compare fields in NLG Form using equality, inequality, greater-than, less-than, and cross-field validation rules.
Compare rules validate relationships between two fields.
They are useful when one field depends on another field or when values must match specific relational constraints.
Common Use Cases
Compare rules are commonly used for:
- password confirmation
- start and end date validation
- minimum and maximum values
- pricing constraints
- score comparison
- quantity validation
- numeric ranges
- matching identifiers
- workflow transitions
- ordered application state
TL;DR
| Rule | Description |
|---|---|
rules.Compare(a, b, rules.OpEQ) | Requires a == b |
rules.Compare(a, b, rules.OpNE) | Requires a != b |
rules.Compare(a, b, rules.OpLT) | Requires a < b |
rules.Compare(a, b, rules.OpLTE) | Requires a <= b |
rules.Compare(a, b, rules.OpGT) | Requires a > b |
rules.Compare(a, b, rules.OpGTE) | Requires a >= b |
rules.CompareWithCode(a, b, op, code) | Same as Compare with a custom error code |
Supported Types
Compare rules support ordered comparable values:
- strings
- integers
- unsigned integers
- float32 / float64
This allows cross-field validation across common application data types.
Comparison Operators
| Operator | Description |
|---|---|
rules.OpEQ | Equal |
rules.OpNE | Not equal |
rules.OpLT | Less than |
rules.OpLTE | Less than or equal |
rules.OpGT | Greater than |
rules.OpGTE | Greater than or equal |
Defining Comparable Fields
Compare validation starts by defining typed fields.
CompareForm := struct {
Password form.StringField[CompareRequest]
ConfirmPassword form.StringField[CompareRequest]
MinPrice form.IntField[CompareRequest]
MaxPrice form.IntField[CompareRequest]
}{
Password: form.Str[CompareRequest]("password", func(r *CompareRequest) string {
return r.Password
}),
ConfirmPassword: form.Str[CompareRequest]("confirm_password", func(r *CompareRequest) string {
return r.ConfirmPassword
}),
MinPrice: form.Int[CompareRequest]("min_price", func(r *CompareRequest) int {
return r.MinPrice
}),
MaxPrice: form.Int[CompareRequest]("max_price", func(r *CompareRequest) int {
return r.MaxPrice
}),
}Compare rules operate on the underlying field definitions:
CompareForm.Password.FieldThis allows reusable cross-field validation logic.
Applying Compare Rules
Compare rules validate relationships between two field values.
return form.Schema[CompareRequest]{
rules.Compare(
CompareForm.Password.Field,
CompareForm.ConfirmPassword.Field,
rules.OpEQ,
),
rules.Compare(
CompareForm.MinPrice.Field,
CompareForm.MaxPrice.Field,
rules.OpLTE,
),
}Rule Examples
Equal (OpEQ)
Requires both values to be equal.
rules.Compare(
CompareForm.Password.Field,
CompareForm.ConfirmPassword.Field,
rules.OpEQ,
)Typical use cases:
- password confirmation
- repeated email fields
- matching identifiers
- confirmation workflows
Not Equal (OpNE)
Requires both values to be different.
rules.Compare(
CompareForm.CurrentPassword.Field,
CompareForm.NewPassword.Field,
rules.OpNE,
)Typical use cases:
- password changes
- unique configuration values
- preventing duplicated state
Less Than (OpLT)
Requires the first value to be less than the second value.
rules.Compare(
CompareForm.MinPrice.Field,
CompareForm.MaxPrice.Field,
rules.OpLT,
)Typical use cases:
- price ranges
- pagination windows
- scoring systems
- numeric boundaries
Less Than or Equal (OpLTE)
Requires the first value to be less than or equal to the second value.
rules.Compare(
CompareForm.MinPrice.Field,
CompareForm.MaxPrice.Field,
rules.OpLTE,
)Useful for inclusive ranges and bounded validation.
Greater Than (OpGT)
Requires the first value to be greater than the second value.
rules.Compare(
CompareForm.MaxPrice.Field,
CompareForm.MinPrice.Field,
rules.OpGT,
)Typical use cases:
- increasing values
- score progression
- upper boundary validation
Greater Than or Equal (OpGTE)
Requires the first value to be greater than or equal to the second value.
rules.Compare(
CompareForm.MaxPrice.Field,
CompareForm.MinPrice.Field,
rules.OpGTE,
)Useful for inclusive upper bounds.
CompareWithCode
Adds a custom validation code to compare validation failures.
const CodePasswordsMismatch = form.Code("passwords_mismatch")
rules.CompareWithCode(
CompareForm.Password.Field,
CompareForm.ConfirmPassword.Field,
rules.OpEQ,
CodePasswordsMismatch,
)Custom error codes are useful for frontend validation contracts and standardized API responses.
Schema Example
package main
import (
"github.com/netlifeguru/form"
"github.com/netlifeguru/form/rules"
)
const (
CodePasswordsMismatch = form.Code("passwords_mismatch")
CodeInvalidRange = form.Code("invalid_range")
)
type CompareRequest struct {
Password string `json:"password"`
ConfirmPassword string `json:"confirm_password"`
MinPrice int `json:"min_price"`
MaxPrice int `json:"max_price"`
}
func CompareSchema() form.Schema[CompareRequest] {
CompareForm := struct {
Password form.StringField[CompareRequest]
ConfirmPassword form.StringField[CompareRequest]
MinPrice form.IntField[CompareRequest]
MaxPrice form.IntField[CompareRequest]
}{
Password: form.Str[CompareRequest]("password", func(r *CompareRequest) string {
return r.Password
}),
ConfirmPassword: form.Str[CompareRequest]("confirm_password", func(r *CompareRequest) string {
return r.ConfirmPassword
}),
MinPrice: form.Int[CompareRequest]("min_price", func(r *CompareRequest) int {
return r.MinPrice
}),
MaxPrice: form.Int[CompareRequest]("max_price", func(r *CompareRequest) int {
return r.MaxPrice
}),
}
return form.Schema[CompareRequest]{
rules.CompareWithCode(
CompareForm.Password.Field,
CompareForm.ConfirmPassword.Field,
rules.OpEQ,
CodePasswordsMismatch,
),
rules.CompareWithCode(
CompareForm.MinPrice.Field,
CompareForm.MaxPrice.Field,
rules.OpLTE,
CodeInvalidRange,
),
}
}HTTP Example
main.go
package main
import (
"encoding/json"
"fmt"
"log/slog"
"net/http"
"os"
"github.com/netlifeguru/form"
"github.com/netlifeguru/form/httpform"
"github.com/netlifeguru/router"
)
func main() {
r := router.New()
r.HandleFunc("/compare-rules", "POST", func(w http.ResponseWriter, req *http.Request, ctx *router.Context) {
var in CompareRequest
if !httpform.BindAndValidate(w, req, &in, CompareSchema(), 1<<20) {
fmt.Println("compare validation failed")
return
}
fmt.Println("compare validation passed:", in)
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{
"message": "compare validation passed",
"data": in,
})
})
validPayload := map[string]any{
"password": "secret123",
"confirm_password": "secret123",
"min_price": 10,
"max_price": 100,
}
invalidPayload := map[string]any{
"password": "secret123",
"confirm_password": "secret321",
"min_price": 100,
"max_price": 10,
}
fmt.Println("\n--- Valid request ---")
form.SendTestPost(":8080/compare-rules", validPayload)
fmt.Println("\n--- Invalid request ---")
form.SendTestPost(":8080/compare-rules", invalidPayload)
if err := r.ListenAndServe(8080); err != nil {
slog.Error("failed to start server", "error", err)
os.Exit(1)
}
}Notes
- Compare rules validate relationships between two field values.
- Validation errors are attached to the first field passed into the comparison.
- Compare rules work with ordered comparable values.
- Compare validation is useful for cross-field consistency and bounded ranges.
- Unknown comparison operators always fail validation.
- Custom validation codes are recommended for frontend-facing APIs.
- Compare validation remains explicit and independent from HTTP or JSON transport layers.
Generic Rules
Learn how to validate generic comparable values in NLG Form using reusable allowed-value rules for strings, integers, booleans, enums, statuses, plans, and custom validation codes.
Optional Validation
Learn how optional validation works in NLG Form using OptionalString, OptionalPtr, OptionalSlice, nullable fields, and conditional validation pipelines.