func IsZero(sub Subject) error {
	if !isZero(sub) {
		return xo.SF("not zero")
	}
	return nil
}
IsZero will check if the provided value is zero. It will determine zeroness
using IsZero() or Zero() if implemented. A nil pointer, slice, array or map
is also considered as zero.
package stick
import (
	"errors"
	"fmt"
	"reflect"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf8"
	"github.com/256dpi/xo"
	"github.com/asaskevich/govalidator"
)
type Validatable interface {
	Validate() error
}
type NoValidation struct{}
func (*NoValidation) Validate() error {
	return nil
}
type ValidationError map[error][]string
func (e ValidationError) Error() string {
	var messages []string
	for err, path := range e {
		msg := "error"
		if xo.IsSafe(err) {
			msg = err.Error()
		}
		messages = append(messages, fmt.Sprintf("%s: %s", strings.Join(path, "."), msg))
	}
	sort.Strings(messages)
	err := strings.Join(messages, "; ")
	return err
}
type Validator struct {
	value interface{}
	path  []string
	error ValidationError
}
func Validate(v interface{}, fn func(v *Validator)) error {
	val := &Validator{value: v}
	fn(val)
	return val.Error()
}
func (v *Validator) Nest(field string, fn func()) {
	v.path = append(v.path, field)
	fn()
	v.path = v.path[:len(v.path)-1]
}
func (v *Validator) Value(name string, optional bool, rules ...Rule) {
	value := MustGetRaw(v.value, name)
	sub := Subject{
		IValue: value.Interface(),
		RValue: value,
	}
	if optional {
		if sub.RValue.Kind() != reflect.Ptr {
			panic("stick: expected pointer")
		}
		if sub.RValue.IsNil() {
			return
		}
		sub.RValue = sub.RValue.Elem()
		sub.IValue = sub.RValue.Interface()
	}
	for _, rule := range rules {
		err := rule(sub)
		if err != nil {
			v.Report(name, err)
		}
	}
}
func (v *Validator) Items(name string, rules ...Rule) {
	slice := MustGetRaw(v.value, name)
	if slice.Kind() != reflect.Slice && slice.Kind() != reflect.Array {
		panic("stick: expected array/slice")
	}
	v.Nest(name, func() {
		for i := 0; i < slice.Len(); i++ {
			item := slice.Index(i)
			sub := Subject{
				IValue: item.Interface(),
				RValue: item,
			}
			for _, rule := range rules {
				err := rule(sub)
				if err != nil {
					v.Report(strconv.Itoa(i), err)
				}
			}
		}
	})
}
func (v *Validator) Report(name string, err error) {
	if v.error == nil {
		v.error = ValidationError{}
	}
	var valError ValidationError
	if errors.As(err, &valError) {
		for err, pth := range valError {
			path := append([]string{}, v.path...)
			path = append(path, name)
			path = append(path, pth...)
			v.error[err] = path
		}
		return
	}
	path := append([]string{}, v.path...)
	path = append(path, name)
	v.error[xo.W(err)] = path
}
func (v *Validator) Error() error {
	if v.error != nil {
		return xo.SW(v.error)
	}
	return nil
}
type Subject struct {
	IValue interface{}
	RValue reflect.Value
}
func (s *Subject) Unwrap() bool {
	var unwrapped bool
	for s.RValue.Kind() == reflect.Ptr {
		if s.RValue.IsNil() {
			return false
		}
		s.RValue = s.RValue.Elem()
		unwrapped = true
	}
	if unwrapped {
		s.IValue = s.RValue.Interface()
	}
	return true
}
func (s *Subject) Reference() Subject {
	addr := s.RValue.Addr()
	return Subject{
		IValue: addr.Interface(),
		RValue: addr,
	}
}
type Rule func(sub Subject) error