From 312945f30aba5629cca2a9ca90f665a82589368b Mon Sep 17 00:00:00 2001 From: Onur Cinar Date: Fri, 16 Jun 2023 15:17:39 -0700 Subject: [PATCH] Min and Max added. (#35) Fixes #17 Fixes #18 --- README.md | 2 ++ checker.go | 16 +++++++++++++ checker_test.go | 28 ++++++++++++++++++++++ doc/checkers/max.md | 30 ++++++++++++++++++++++++ doc/checkers/min.md | 30 ++++++++++++++++++++++++ max.go | 40 +++++++++++++++++++++++++++++++ max_test.go | 57 +++++++++++++++++++++++++++++++++++++++++++++ min.go | 40 +++++++++++++++++++++++++++++++ min_test.go | 57 +++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 300 insertions(+) create mode 100644 doc/checkers/max.md create mode 100644 doc/checkers/min.md create mode 100644 max.go create mode 100644 max_test.go create mode 100644 min.go create mode 100644 min_test.go diff --git a/README.md b/README.md index bd0f3e1..91e787d 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,9 @@ type Person struct { This package currently provides the following checkers: +- [max](doc/checkers/max.md) checks if the given value is less than the given maximum. - [max-length](doc/checkers/maxlength.md) checks if the length of the given value is less than the given maximum length. +- [min](doc/checkers/min.md) checks if the given value is greather than the given minimum. - [min-length](doc/checkers/minlength.md) checks if the length of the given value is greather than the given minimum length. - [required](doc/checkers/required.md) checks if the required value is provided. - [same](doc/checkers/same.md) checks if the given value is equal to the value of the field with the given name. diff --git a/checker.go b/checker.go index 687f4d4..2763402 100644 --- a/checker.go +++ b/checker.go @@ -31,7 +31,9 @@ const ResultValid Result = "VALID" // makers provides mapping to maker function for the checkers. var makers = map[string]MakeFunc{ + CheckerMax: makeMax, CheckerMaxLength: makeMaxLength, + CheckerMin: makeMin, CheckerMinLength: makeMinLength, CheckerRequired: makeRequired, CheckerSame: makeSame, @@ -120,3 +122,17 @@ func initCheckers(config string) []CheckFunc { return checkers } + +// numberOf gives value's numerical value given that it is either an int or a float. +func numberOf(value reflect.Value) float64 { + switch value.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return float64(value.Int()) + + case reflect.Float32, reflect.Float64: + return value.Float() + + default: + panic("expecting int or float") + } +} diff --git a/checker_test.go b/checker_test.go index 46f2090..ab72455 100644 --- a/checker_test.go +++ b/checker_test.go @@ -132,6 +132,34 @@ func TestCheckNestedStruct(t *testing.T) { } } +func TestNumberOfInvalid(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Fail() + } + }() + + s := "invalid" + + numberOf(reflect.ValueOf(s)) +} + +func TestNumberOfInt(t *testing.T) { + n := 10 + + if numberOf(reflect.ValueOf(n)) != float64(n) { + t.Fail() + } +} + +func TestNumberOfFloat(t *testing.T) { + n := float64(10.10) + + if numberOf(reflect.ValueOf(n)) != n { + t.Fail() + } +} + func BenchmarkCheck(b *testing.B) { type Address struct { Street string `checkers:"required"` diff --git a/doc/checkers/max.md b/doc/checkers/max.md new file mode 100644 index 0000000..e780768 --- /dev/null +++ b/doc/checkers/max.md @@ -0,0 +1,30 @@ +# Max Checker + +The ```max``` checker checks if the given ```int``` or ```float``` value is less than the given maximum. If the value is above the maximum, the checker will return the ```NOT_MAX``` result. Here is an example: + +```golang +type Order struct { + Quantity int `checkers:"max:10"` +} + +order := &Order{ + Quantity: 5, +} + +mistakes, valid := Check(order) +if !valid { + // Send the mistakes back to the user +} +``` + +In your custom checkers, you can call the ```max``` checker function ```IsMax``` to validate the user input. Here is an example: + +```golang +quantity := 5 + +result := IsMax(quantity, 10 ) + +if result != ResultValid { + // Send the mistakes back to the user +} +``` diff --git a/doc/checkers/min.md b/doc/checkers/min.md new file mode 100644 index 0000000..0b05725 --- /dev/null +++ b/doc/checkers/min.md @@ -0,0 +1,30 @@ +# Min Checker + +The ```min``` checker checks if the given ```int``` or ```float``` value is greather than the given minimum. If the value is below the minimum, the checker will return the ```NOT_MIN``` result. Here is an example: + +```golang +type User struct { + Age int `checkers:"min:21"` +} + +user := &User{ + Age: 45, +} + +mistakes, valid := Check(user) +if !valid { + // Send the mistakes back to the user +} +``` + +In your custom checkers, you can call the ```min``` checker function ```IsMin``` to validate the user input. Here is an example: + +```golang +age := 45 + +result := IsMin(age, 21) + +if result != ResultValid { + // Send the mistakes back to the user +} +``` diff --git a/max.go b/max.go new file mode 100644 index 0000000..4583ae3 --- /dev/null +++ b/max.go @@ -0,0 +1,40 @@ +package checker + +import ( + "reflect" + "strconv" +) + +// CheckerMax is the name of the checker. +const CheckerMax = "max" + +// ResultNotMax indicates that the given value is above the defined maximum. +const ResultNotMax = "NOT_MIN" + +// IsMax checks if the given value is below than the given maximum. +func IsMax(value interface{}, max float64) Result { + return checkMax(reflect.Indirect(reflect.ValueOf(value)), reflect.ValueOf(nil), max) +} + +// makeMax makes a checker function for the max checker. +func makeMax(config string) CheckFunc { + max, err := strconv.ParseFloat(config, 64) + if err != nil { + panic("unable to parse max") + } + + return func(value, parent reflect.Value) Result { + return checkMax(value, parent, max) + } +} + +// checkMax checks if the given value is less than the given maximum. +func checkMax(value, _ reflect.Value, max float64) Result { + n := numberOf(value) + + if n > max { + return ResultNotMax + } + + return ResultValid +} diff --git a/max_test.go b/max_test.go new file mode 100644 index 0000000..4c3afaf --- /dev/null +++ b/max_test.go @@ -0,0 +1,57 @@ +package checker + +import "testing" + +func TestIsMaxValid(t *testing.T) { + n := 5 + + if IsMax(n, 10) != ResultValid { + t.Fail() + } +} + +func TestCheckMaxInvalidConfig(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Fail() + } + }() + + type Order struct { + Quantity int `checkers:"max:AB"` + } + + order := &Order{} + + Check(order) +} + +func TestCheckMaxValid(t *testing.T) { + type Order struct { + Quantity int `checkers:"max:10"` + } + + order := &Order{ + Quantity: 5, + } + + _, valid := Check(order) + if !valid { + t.Fail() + } +} + +func TestCheckMaxInvalid(t *testing.T) { + type Order struct { + Quantity int `checkers:"max:10"` + } + + order := &Order{ + Quantity: 20, + } + + _, valid := Check(order) + if valid { + t.Fail() + } +} diff --git a/min.go b/min.go new file mode 100644 index 0000000..15ea1bd --- /dev/null +++ b/min.go @@ -0,0 +1,40 @@ +package checker + +import ( + "reflect" + "strconv" +) + +// CheckerMin is the name of the checker. +const CheckerMin = "min" + +// ResultNotMin indicates that the given value is below the defined minimum. +const ResultNotMin = "NOT_MIN" + +// IsMin checks if the given value is above than the given minimum. +func IsMin(value interface{}, min float64) Result { + return checkMin(reflect.Indirect(reflect.ValueOf(value)), reflect.ValueOf(nil), min) +} + +// makeMin makes a checker function for the min checker. +func makeMin(config string) CheckFunc { + min, err := strconv.ParseFloat(config, 64) + if err != nil { + panic("unable to parse min") + } + + return func(value, parent reflect.Value) Result { + return checkMin(value, parent, min) + } +} + +// checkMin checks if the given value is greather than the given minimum. +func checkMin(value, _ reflect.Value, min float64) Result { + n := numberOf(value) + + if n < min { + return ResultNotMin + } + + return ResultValid +} diff --git a/min_test.go b/min_test.go new file mode 100644 index 0000000..dbe0d47 --- /dev/null +++ b/min_test.go @@ -0,0 +1,57 @@ +package checker + +import "testing" + +func TestIsMinValid(t *testing.T) { + n := 45 + + if IsMin(n, 21) != ResultValid { + t.Fail() + } +} + +func TestCheckMinInvalidConfig(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Fail() + } + }() + + type User struct { + Age int `checkers:"min:AB"` + } + + user := &User{} + + Check(user) +} + +func TestCheckMinValid(t *testing.T) { + type User struct { + Age int `checkers:"min:21"` + } + + user := &User{ + Age: 45, + } + + _, valid := Check(user) + if !valid { + t.Fail() + } +} + +func TestCheckMinInvalid(t *testing.T) { + type User struct { + Age int `checkers:"min:21"` + } + + user := &User{ + Age: 18, + } + + _, valid := Check(user) + if valid { + t.Fail() + } +}