Move to yaml-based configuration
This commit updates the configuration to be yaml-based and updates the configuration to read in a yaml file.
This commit is contained in:
parent
568c005d15
commit
945329a080
70 changed files with 1150 additions and 350 deletions
131
pkg/validator/vmap.go
Normal file
131
pkg/validator/vmap.go
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
package validator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/tkw1536/goprogram/lib/reflectx"
|
||||
)
|
||||
|
||||
// Collection represents a set of validators.
|
||||
// The zero value is not ready to use; it should be created using make().
|
||||
//
|
||||
// A validator is a non-nil function with signature func(value *F, dflt string) error.
|
||||
// Here F is the type of a value of a field.
|
||||
// The value is the initialized value to be validated.
|
||||
// The validator may perform abitrary normalization on the value.
|
||||
// dflt is the default value (read from the 'default' tag).
|
||||
// error should be an appropriate error that occured.
|
||||
//
|
||||
// A validator function is applied by calling it.
|
||||
type Collection map[string]any
|
||||
|
||||
// Add adds a Validator to the provided collection of validators.
|
||||
// Any previously validator of the same name is overwritten.
|
||||
func Add[F any](coll Collection, name string, validator func(value *F, dflt string) error) {
|
||||
coll[name] = validator
|
||||
}
|
||||
|
||||
// AddSlice adds a Validator to the provided collection of validators that validates a slice of the given type. The default is seperated by seperator.
|
||||
func AddSlice[F any](coll Collection, name string, sep string, validator func(value *F, dflt string) error) {
|
||||
Add(coll, name, func(value *[]F, dflt string) error {
|
||||
// some value is set, so we do not need to set the default!
|
||||
if *value != nil {
|
||||
for i := range *value {
|
||||
if err := validator(&(*value)[i], ""); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no default provided => set if to an empty slice
|
||||
if dflt == "" {
|
||||
*value = make([]F, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
// some default provided => iterate over the underlying validator
|
||||
dflts := strings.Split(dflt, sep)
|
||||
*value = make([]F, len(dflts))
|
||||
for i := range *value {
|
||||
if err := validator(&(*value)[i], dflts[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
errTyp = reflectx.TypeOf[error]()
|
||||
strTyp = reflectx.TypeOf[string]()
|
||||
)
|
||||
|
||||
// UnknownValidator is an error returned from Validate if a validator does not exist
|
||||
type UnknownValidator string
|
||||
|
||||
func (uv UnknownValidator) Error() string {
|
||||
return fmt.Sprintf("unknown validator %q", string(uv))
|
||||
}
|
||||
|
||||
// NotAValidator is an error returned from Validate if an entry in the validators map is not a validator
|
||||
type NotAValidator string
|
||||
|
||||
func (nv NotAValidator) Error() string {
|
||||
return fmt.Sprintf("entry %q in validators is not a valiator", string(nv))
|
||||
}
|
||||
|
||||
// IncompatibleValidator is returned when a validator in the validators map is incompatible
|
||||
type IncompatibleValidator struct {
|
||||
Validator string
|
||||
GotType reflect.Type
|
||||
ExpectedType reflect.Type
|
||||
}
|
||||
|
||||
func (iv IncompatibleValidator) Error() string {
|
||||
return fmt.Sprintf("validator %q: got type %s, expected type %s", iv.Validator, iv.GotType, iv.ExpectedType)
|
||||
}
|
||||
|
||||
// Call calls the validator with the given name, on the given value, and with the provided default.
|
||||
// See documentation of [Validate] for details.
|
||||
func (coll Collection) Call(name string, field reflect.Value, dflt string) error {
|
||||
validator, ok := coll[name]
|
||||
if !ok {
|
||||
return UnknownValidator(name)
|
||||
}
|
||||
|
||||
// get the type of the validator
|
||||
vFunc := reflect.ValueOf(validator)
|
||||
vTyp := vFunc.Type()
|
||||
|
||||
// ensure that vTyp is of type func(*F,string) error
|
||||
// where T is the type of the field
|
||||
//
|
||||
// - the first if assumes checks for some type F
|
||||
// - the second if checks if the F is the right one
|
||||
if validator == nil || vTyp.Kind() != reflect.Func || // func
|
||||
vTyp.NumIn() != 2 || vTyp.In(0).Kind() != reflect.Pointer || vTyp.In(1) != strTyp || // (*F,string)
|
||||
vTyp.NumOut() != 1 || vTyp.Out(0) != errTyp { // error
|
||||
return NotAValidator(name)
|
||||
}
|
||||
if vTyp.In(0).Elem() != field.Type() { // the correct *F
|
||||
return IncompatibleValidator{
|
||||
Validator: name,
|
||||
GotType: vTyp.In(0).Elem(),
|
||||
ExpectedType: field.Type(),
|
||||
}
|
||||
}
|
||||
|
||||
// call the validator function, and return an error
|
||||
results := vFunc.Call([]reflect.Value{field.Addr(), reflect.ValueOf(dflt)})
|
||||
|
||||
// turn the result into an error
|
||||
// NOTE: We can't just .(error) here because that panic()s on err == nil
|
||||
err := results[0].Interface()
|
||||
if err, ok := err.(error); ok {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue