Skip to content

Commit

Permalink
rework rules structure
Browse files Browse the repository at this point in the history
  • Loading branch information
aacebo committed Apr 6, 2024
1 parent 6ed0b75 commit 00d72c4
Show file tree
Hide file tree
Showing 12 changed files with 287 additions and 171 deletions.
72 changes: 72 additions & 0 deletions context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package owl

import (
"reflect"
"strings"
)

type context struct {
rule string
schema map[string]string
root reflect.Value
parent reflect.Value
value reflect.Value
field reflect.StructField
hasFormat func(string) bool
format func(string, string) error
}

func (self context) Root() reflect.Value {
return self.root
}

func (self context) Parent() reflect.Value {
return self.parent
}

func (self context) Rule() string {
return self.rule
}

func (self context) Param() string {
return self.schema[self.rule]
}

func (self context) RuleParam(name string) (string, bool) {
v, ok := self.schema[name]
return v, ok
}

func (self context) Name() string {
name := self.field.Name

if tag := self.field.Tag.Get("json"); tag != "" {
parts := strings.Split(tag, ",")

if parts[0] != "-" {
name = parts[0]
}
}

return name
}

func (self context) FieldName() string {
return self.field.Name
}

func (self context) Value() reflect.Value {
return self.value
}

func (self context) CoerceValue() reflect.Value {
return reflect.Indirect(self.value)
}

func (self context) HasFormat(name string) bool {
return self.hasFormat(name)
}

func (self context) Format(name string, text string) error {
return self.format(name, text)
}
4 changes: 3 additions & 1 deletion global.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package owl

import "github.com/aacebo/owl/rules"

var validator = New()

func AddRule(name string, rule Rule) {
func AddRule(name string, rule rules.Rule) {
validator.AddRule(name, rule)
}

Expand Down
17 changes: 0 additions & 17 deletions rule.go

This file was deleted.

38 changes: 38 additions & 0 deletions rules/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package rules

import "reflect"

type Context interface {
// get the root struct
Root() reflect.Value

// get the parent struct
Parent() reflect.Value

// get the field value
Value() reflect.Value

// get coerced field value, indirecting pointers and interfaces
CoerceValue() reflect.Value

// get the field "json" name if exists, otherwise get the struct field name
Name() string

// get the struct field name
FieldName() string

// get the current rule name
Rule() string

// get the current rules parameter
Param() string

// get a rules parameter
RuleParam(name string) (string, bool)

// check if instance has formatter
HasFormat(name string) bool

// format text using formatter
Format(name string, text string) error
}
47 changes: 23 additions & 24 deletions rules/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,65 +6,64 @@ import (
"strconv"
)

type Default struct{}

func (self Default) Select(schema map[string]string, parent reflect.Value, value reflect.Value) bool {
return true
}

func (self Default) Validate(schema map[string]string, parent reflect.Value, value reflect.Value) (reflect.Value, []error) {
func Default(ctx Context) []error {
errs := []error{}
config, ok := schema["default"]
value := ctx.Value()
param := ctx.Param()

if param == "" {
errs = append(errs, errors.New("param is required"))
return errs
}

if !ok || config == "" {
errs = append(errs, errors.New("config is required"))
return value, errs
if !value.CanAddr() {
errs = append(errs, errors.New("cannot be addressed"))
return errs
}

if value.Kind() == reflect.Pointer && value.IsZero() {
t := value.Type()
ptr := reflect.New(t.Elem())

if t.Elem().ConvertibleTo(reflect.TypeFor[int]()) {
v, err := strconv.ParseInt(config, 10, 64)
v, err := strconv.ParseInt(param, 10, 64)

if err != nil {
errs = append(errs, errors.New("config must be an int"))
errs = append(errs, errors.New("param must be an int"))
return errs
}

ptr.Elem().Set(reflect.ValueOf(v).Convert(t.Elem()))
return ptr, errs
}

if t.Elem().ConvertibleTo(reflect.TypeFor[float64]()) {
v, err := strconv.ParseFloat(config, 64)
v, err := strconv.ParseFloat(param, 64)

if err != nil {
errs = append(errs, errors.New("config must be an float"))
errs = append(errs, errors.New("param must be an float"))
return errs
}

ptr.Elem().Set(reflect.ValueOf(v).Convert(t.Elem()))
return ptr, errs
}

if t.Elem().Kind() == reflect.Bool {
v, err := strconv.ParseBool(config)
v, err := strconv.ParseBool(param)

if err != nil {
errs = append(errs, errors.New("config must be a bool"))
errs = append(errs, errors.New("param must be a bool"))
return errs
}

ptr.Elem().SetBool(v)
return ptr, errs
}

if t.Elem().Kind() == reflect.String {
ptr.Elem().SetString(config)
return ptr, errs
ptr.Elem().SetString(param)
}

errs = append(errs, errors.New("default can only be used on primitive types"))
value.Set(ptr)
}

return value, errs
return errs
}
37 changes: 19 additions & 18 deletions rules/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,37 @@ import (
"reflect"
)

type Format struct {
HasFormat func(string) bool
Format func(string, string) error
}
func Format(ctx Context) []error {
errs := []error{}
value := ctx.CoerceValue()
param := ctx.Param()

func (self Format) Select(schema map[string]string, parent reflect.Value, value reflect.Value) bool {
return value.Kind() == reflect.String
}
if value.Kind() == reflect.Invalid {
return errs
}

func (self Format) Validate(schema map[string]string, parent reflect.Value, value reflect.Value) (reflect.Value, []error) {
errs := []error{}
config, ok := schema["format"]
if value.Kind() != reflect.String {
errs = append(errs, errors.New(`can only be used on "string" type`))
return errs
}

if !ok {
errs = append(errs, errors.New("empty config"))
return value, errs
if param == "" {
errs = append(errs, errors.New("param is required"))
return errs
}

if !self.HasFormat(config) {
if !ctx.HasFormat(param) {
errs = append(errs, errors.New(fmt.Sprintf(
`format "%s" not found`,
config,
param,
)))

return value, errs
return errs
}

if err := self.Format(config, value.String()); err != nil {
if err := ctx.Format(param, value.String()); err != nil {
errs = append(errs, err)
}

return value, errs
return errs
}
50 changes: 28 additions & 22 deletions rules/max.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,47 @@ import (
"strconv"
)

type Max struct{}
func Max(ctx Context) []error {
errs := []error{}
value := ctx.CoerceValue()

func (self Max) Select(schema map[string]string, parent reflect.Value, value reflect.Value) bool {
return value.CanFloat() || value.CanConvert(floatType) || value.Kind() == reflect.String
}
if value.Kind() == reflect.Invalid {
return errs
}

func (self Max) Validate(schema map[string]string, parent reflect.Value, value reflect.Value) (reflect.Value, []error) {
errs := []error{}
if !value.CanFloat() && !value.CanConvert(floatType) && value.Kind() != reflect.String {
errs = append(errs, errors.New("invalid type"))
return errs
}

if _, ok := schema["max"]; !ok {
if ctx.Param() == "" {
errs = append(errs, errors.New("must be greater than or equal to 0"))
return value, errs
return errs
}

if value.Kind() == reflect.String {
return self.validateString(schema, value)
return maxString(ctx)
}

return self.validateNumber(schema, value)
return maxNumber(ctx)
}

func (self Max) validateNumber(schema map[string]string, value reflect.Value) (reflect.Value, []error) {
func maxNumber(ctx Context) []error {
errs := []error{}
max, err := strconv.ParseFloat(schema["max"], 64)
value := ctx.CoerceValue()
max, err := strconv.ParseFloat(ctx.Param(), 64)

if err != nil {
errs = append(errs, err)
return value, errs
return errs
}

if max < 0 {
errs = append(errs, errors.New("must be greater than or equal to 0"))
return value, errs
return errs
}

if v, ok := schema["min"]; ok {
if v, ok := ctx.RuleParam("min"); ok {
min, err := strconv.ParseFloat(v, 64)

if err == nil && max < min {
Expand All @@ -62,24 +67,25 @@ func (self Max) validateNumber(schema map[string]string, value reflect.Value) (r
)))
}

return value, errs
return errs
}

func (self Max) validateString(schema map[string]string, value reflect.Value) (reflect.Value, []error) {
func maxString(ctx Context) []error {
errs := []error{}
max, err := strconv.ParseInt(schema["max"], 10, 64)
value := ctx.CoerceValue()
max, err := strconv.ParseInt(ctx.Param(), 10, 64)

if err != nil {
errs = append(errs, err)
return value, errs
return errs
}

if max < 0 {
errs = append(errs, errors.New("config must be greater than or equal to 0"))
return value, errs
return errs
}

if v, ok := schema["min"]; ok {
if v, ok := ctx.RuleParam("min"); ok {
min, err := strconv.ParseInt(v, 10, 64)

if err == nil && max < min {
Expand All @@ -96,5 +102,5 @@ func (self Max) validateString(schema map[string]string, value reflect.Value) (r
)))
}

return value, errs
return errs
}
Loading

0 comments on commit 00d72c4

Please sign in to comment.