Skip to content

Commit

Permalink
Dev when and skip on empty (#19)
Browse files Browse the repository at this point in the history
* Dev when and skip on empty
  • Loading branch information
raoptimus authored Mar 7, 2024
1 parent e28cc8c commit b0deebf
Show file tree
Hide file tree
Showing 30 changed files with 1,493 additions and 371 deletions.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ test:
-bench=$(TEST_PATTERN) \
-timeout=2m

lint: ## Run lint
golangci-lint run --timeout 5m

bench:
@[ -d ${REPORTS_DIR} ] || mkdir -p ${REPORTS_DIR}
@rm -rf ${REPORTS_DIR}/*
Expand Down
6 changes: 3 additions & 3 deletions basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@ func toString(v any) (string, bool) {
return "", false
}

func hasRequiredRule(rules []Rule) (Required, bool) {
func hasRequiredRule(rules []Rule) (*Required, bool) {
for _, r := range rules {
if v, ok := r.(Required); ok {
if v, ok := r.(*Required); ok {
return v, ok
}
}

return Required{}, false
return nil, false
}
55 changes: 51 additions & 4 deletions callback.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,63 @@ import (

type CallbackFunc[T any] func(ctx context.Context, value T) error
type Callback[T any] struct {
f CallbackFunc[T]
f CallbackFunc[T]
whenFunc WhenFunc
skipEmpty bool
skipError bool
}

func NewCallback[T any](f CallbackFunc[T]) Callback[T] {
return Callback[T]{
func NewCallback[T any](f CallbackFunc[T]) *Callback[T] {
return &Callback[T]{
f: f,
}
}

func (c Callback[T]) ValidateValue(ctx context.Context, value any) error {
func (r *Callback[T]) When(v WhenFunc) *Callback[T] {
rc := *r
rc.whenFunc = v

return &rc
}

func (r *Callback[T]) when() WhenFunc {
return r.whenFunc
}

func (r *Callback[T]) setWhen(v WhenFunc) {
r.whenFunc = v
}

func (r *Callback[T]) SkipOnEmpty() *Callback[T] {
rc := *r
rc.skipEmpty = true

return &rc
}

func (r *Callback[T]) skipOnEmpty() bool {
return r.skipEmpty
}

func (r *Callback[T]) setSkipOnEmpty(v bool) {
r.skipEmpty = v
}

func (r *Callback[T]) SkipOnError() *Callback[T] {
rs := *r
rs.skipError = true

return &rs
}

func (r *Callback[T]) shouldSkipOnError() bool {
return r.skipError
}
func (r *Callback[T]) setSkipOnError(v bool) {
r.skipError = v
}

func (c *Callback[T]) ValidateValue(ctx context.Context, value any) error {
v, ok := value.(T)
if !ok {
var v T
Expand Down
9 changes: 4 additions & 5 deletions callback_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,12 @@ func TestCallback_ValidateValue_Error(t *testing.T) {
rules := RuleSet{
"A": {
NewCallback(func(ctx context.Context, value int) error {
if ds, ok := extractDataSet(ctx); ok {
if obj, ok := ds.Data().(*TestCallback); ok {
if obj.B > value {
return errAMustGreatB
}
if obj, ok := ExtractDataSet[*TestCallback](ctx); ok {
if obj.B > value {
return errAMustGreatB
}
}

return nil
}),
},
Expand Down
97 changes: 76 additions & 21 deletions compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@ type Compare struct {
operator string
message string
operatorIsValid bool
whenFunc WhenFunc
skipEmpty bool
skipError bool
}

func NewCompare(targetValue any, targetAttribute, operator string) Compare {
c := Compare{
func NewCompare(targetValue any, targetAttribute, operator string) *Compare {
c := &Compare{
targetValue: targetValue,
targetAttribute: targetAttribute,
operator: operator,
Expand All @@ -37,11 +40,63 @@ func NewCompare(targetValue any, targetAttribute, operator string) Compare {
default:
c.operatorIsValid = false
}

return c
}

func (c Compare) ValidateValue(ctx context.Context, value any) error {
if !c.operatorIsValid {
func (r *Compare) WithMessage(v string) *Compare {
rc := *r
rc.message = v

return &rc
}

func (r *Compare) When(v WhenFunc) *Compare {
rc := *r
rc.whenFunc = v

return &rc
}

func (r *Compare) when() WhenFunc {
return r.whenFunc
}

func (r *Compare) setWhen(v WhenFunc) {
r.whenFunc = v
}

func (r *Compare) SkipOnEmpty() *Compare {
rc := *r
rc.skipEmpty = true

return &rc
}

func (r *Compare) skipOnEmpty() bool {
return r.skipEmpty
}

func (r *Compare) setSkipOnEmpty(v bool) {
r.skipEmpty = v
}

func (r *Compare) SkipOnError() *Compare {
rs := *r
rs.skipError = true

return &rs
}

func (r *Compare) shouldSkipOnError() bool {
return r.skipError
}
func (r *Compare) setSkipOnError(v bool) {
r.skipError = v
}

func (r *Compare) ValidateValue(ctx context.Context, value any) error {
if !r.operatorIsValid {
return UnknownOperatorError
}

Expand All @@ -50,60 +105,60 @@ func (c Compare) ValidateValue(ctx context.Context, value any) error {
targetValueOrAttr any
err error
)
targetValue = c.targetValue
targetValueOrAttr = c.targetAttribute
targetValue = r.targetValue
targetValueOrAttr = r.targetAttribute

if c.targetValue == nil {
dataSet, ok := extractDataSet(ctx)
if r.targetValue == nil {
dataSet, ok := ExtractDataSet[DataSet](ctx)
if !ok {
return NotExistsDataSetIntoContextError
}
targetValue, err = dataSet.FieldValue(c.targetAttribute)
targetValue, err = dataSet.FieldValue(r.targetAttribute)
if err != nil {
return err
}
targetValueOrAttr = targetValue
}

switch c.operator {
switch r.operator {
case "==":
if c.eq(value, targetValue) {
if r.eq(value, targetValue) {
return nil
}
case "!=":
if !c.eq(value, targetValue) {
if !r.eq(value, targetValue) {
return nil
}
case ">":
if c.gt(value, targetValue) {
if r.gt(value, targetValue) {
return nil
}
case ">=":
if c.eq(value, targetValue) || c.gt(value, targetValue) {
if r.eq(value, targetValue) || r.gt(value, targetValue) {
return nil
}
case "<":
if !c.eq(value, targetValue) && !c.gt(value, targetValue) {
if !r.eq(value, targetValue) && !r.gt(value, targetValue) {
return nil
}
case "<=":
if c.eq(value, targetValue) || !c.gt(value, targetValue) {
if r.eq(value, targetValue) || !r.gt(value, targetValue) {
return nil
}
}

return NewResult().
WithError(
NewValidationError(c.message).
NewValidationError(r.message).
WithParams(map[string]any{
"targetValue": c.targetValue,
"targetAttribute": c.targetAttribute,
"targetValue": r.targetValue,
"targetAttribute": r.targetAttribute,
"targetValueOrAttribute": targetValueOrAttr,
}),
)
}

func (c Compare) eq(a, b any) bool {
func (r *Compare) eq(a, b any) bool {
if ia, ok := a.(int); ok {
if ib, ok := b.(int); ok {
return ia == ib
Expand Down Expand Up @@ -143,7 +198,7 @@ func (c Compare) eq(a, b any) bool {
return a == b
}

func (c Compare) gt(a, b any) bool {
func (r *Compare) gt(a, b any) bool {
if ia, ok := a.(int); ok {
if ib, ok := b.(int); ok {
return ia > ib
Expand Down
77 changes: 71 additions & 6 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type Key uint8

const (
KeyDataSet Key = iota + 1
PreviousRulesErrored
)

type DataSet interface {
Expand All @@ -19,17 +20,81 @@ type DataSet interface {
Data() any
}

type Context struct {
context.Context
ds DataSet
}

func NewContext(ctx context.Context) *Context {
return &Context{Context: ctx}
}

func (c *Context) Value(key any) any {
if key == KeyDataSet {
return c.ds
}

return c.Context.Value(key)
}

func (c *Context) withDataSet(ds DataSet) *Context {
cc := *c
cc.ds = ds

return &cc
}

func (c *Context) dataSet() (DataSet, bool) {
return c.ds, c.ds != nil
}

func DataSetFromContext[T DataSet](ctx *Context) (T, bool) {
if ds, ok := ctx.dataSet(); ok {
if dsT, ok2 := ds.(T); ok2 {
return dsT, true
}
}
var v T

return v, false
}

// todo: write funcs if context.Context interface

func withDataSet(ctx context.Context, ds DataSet) context.Context {
return context.WithValue(ctx, KeyDataSet, ds)
return NewContext(ctx).withDataSet(ds)
//return context.WithValue(ctx, KeyDataSet, ds)
}

func extractDataSet(ctx context.Context) (DataSet, bool) {
func ExtractDataSet[T any](ctx context.Context) (T, bool) {
var v T
if ctx == nil {
return nil, false
return v, false
}

ds, ok := ctx.Value(KeyDataSet).(DataSet)
if !ok {
return v, false
}

if dst, ok := ds.(T); ok {
return dst, true
}
if ds, ok := ctx.Value(KeyDataSet).(DataSet); ok {
return ds, true

if dt, ok := ds.Data().(T); ok {
return dt, true
}

return nil, false
return v, true
}

func withPreviousRulesErrored(ctx context.Context) context.Context {
return context.WithValue(ctx, PreviousRulesErrored, true)
}

func previousRulesErrored(ctx context.Context) bool {
if y, ok := ctx.Value(PreviousRulesErrored).(bool); ok {
return y
}
return false
}
Loading

0 comments on commit b0deebf

Please sign in to comment.