Gte and Lte checks are added. (#161)
# Describe Request Greater than or equal tp (Gte) and less than or equal to (Lte) checks are added. # Change Type New checks.
This commit is contained in:
parent
21065d3d76
commit
1188cff9b4
9 changed files with 460 additions and 2 deletions
40
DOC.md
40
DOC.md
|
|
@ -29,6 +29,7 @@ Package v2 Checker is a Go library for validating user input through checker rul
|
|||
- [func IsDiscoverCreditCard\(number string\) \(string, error\)](<#IsDiscoverCreditCard>)
|
||||
- [func IsEmail\(value string\) \(string, error\)](<#IsEmail>)
|
||||
- [func IsFQDN\(value string\) \(string, error\)](<#IsFQDN>)
|
||||
- [func IsGte\[T cmp.Ordered\]\(value, n T\) \(T, error\)](<#IsGte>)
|
||||
- [func IsHex\(value string\) \(string, error\)](<#IsHex>)
|
||||
- [func IsIP\(value string\) \(string, error\)](<#IsIP>)
|
||||
- [func IsIPv4\(value string\) \(string, error\)](<#IsIPv4>)
|
||||
|
|
@ -36,6 +37,7 @@ Package v2 Checker is a Go library for validating user input through checker rul
|
|||
- [func IsISBN\(value string\) \(string, error\)](<#IsISBN>)
|
||||
- [func IsJcbCreditCard\(number string\) \(string, error\)](<#IsJcbCreditCard>)
|
||||
- [func IsLUHN\(value string\) \(string, error\)](<#IsLUHN>)
|
||||
- [func IsLte\[T cmp.Ordered\]\(value, n T\) \(T, error\)](<#IsLte>)
|
||||
- [func IsMAC\(value string\) \(string, error\)](<#IsMAC>)
|
||||
- [func IsMasterCardCreditCard\(number string\) \(string, error\)](<#IsMasterCardCreditCard>)
|
||||
- [func IsRegexp\(expression, value string\) \(string, error\)](<#IsRegexp>)
|
||||
|
|
@ -80,6 +82,24 @@ const (
|
|||
|
||||
## Variables
|
||||
|
||||
<a name="ErrGte"></a>
|
||||
|
||||
```go
|
||||
var (
|
||||
// ErrGte indicates that the value is not greater than or equal to the given value.
|
||||
ErrGte = NewCheckError("NOT_GTE")
|
||||
)
|
||||
```
|
||||
|
||||
<a name="ErrLte"></a>
|
||||
|
||||
```go
|
||||
var (
|
||||
// ErrLte indicates that the value is not less than or equal to the given value.
|
||||
ErrLte = NewCheckError("NOT_LTE")
|
||||
)
|
||||
```
|
||||
|
||||
<a name="ErrMaxLen"></a>
|
||||
|
||||
```go
|
||||
|
|
@ -707,6 +727,15 @@ func main() {
|
|||
</p>
|
||||
</details>
|
||||
|
||||
<a name="IsGte"></a>
|
||||
## func [IsGte](<https://github.com/cinar/checker/blob/main/gte.go#L25>)
|
||||
|
||||
```go
|
||||
func IsGte[T cmp.Ordered](value, n T) (T, error)
|
||||
```
|
||||
|
||||
IsGte checks if the value is greater than or equal to the given value.
|
||||
|
||||
<a name="IsHex"></a>
|
||||
## func [IsHex](<https://github.com/cinar/checker/blob/main/hex.go#L23>)
|
||||
|
||||
|
|
@ -944,6 +973,15 @@ func main() {
|
|||
</p>
|
||||
</details>
|
||||
|
||||
<a name="IsLte"></a>
|
||||
## func [IsLte](<https://github.com/cinar/checker/blob/main/lte.go#L25>)
|
||||
|
||||
```go
|
||||
func IsLte[T cmp.Ordered](value, n T) (T, error)
|
||||
```
|
||||
|
||||
IsLte checks if the value is less than or equal to the given value.
|
||||
|
||||
<a name="IsMAC"></a>
|
||||
## func [IsMAC](<https://github.com/cinar/checker/blob/main/mac.go#L24>)
|
||||
|
||||
|
|
@ -1173,7 +1211,7 @@ func RegisterLocale(locale string, messages map[string]string)
|
|||
RegisterLocale registers the localized error messages for the given locale.
|
||||
|
||||
<a name="RegisterMaker"></a>
|
||||
## func [RegisterMaker](<https://github.com/cinar/checker/blob/main/maker.go#L51>)
|
||||
## func [RegisterMaker](<https://github.com/cinar/checker/blob/main/maker.go#L53>)
|
||||
|
||||
```go
|
||||
func RegisterMaker(name string, maker MakeCheckFunc)
|
||||
|
|
|
|||
|
|
@ -104,11 +104,13 @@ type Person struct {
|
|||
- [`digits`](DOC.md#IsDigits): Ensures the string contains only digits.
|
||||
- [`email`](DOC.md#IsEmail): Ensures the string is a valid email address.
|
||||
- [`fqdn`](DOC.md#IsFQDN): Ensures the string is a valid fully qualified domain name.
|
||||
- [`hex`](DOC.md#IsHex): Ensures the string contains only hex digits.
|
||||
- [`gte`](DOC.md#IsGte): Ensures the value is greater than or equal to the specified number.
|
||||
- [`hex`](DOC.md#IsHex): Ensures the string contains only hexadecimal digits.
|
||||
- [`ip`](DOC.md#IsIP): Ensures the string is a valid IP address.
|
||||
- [`ipv4`](DOC.md#IsIPv4): Ensures the string is a valid IPv4 address.
|
||||
- [`ipv6`](DOC.md#IsIPv6): Ensures the string is a valid IPv6 address.
|
||||
- [`isbn`](DOC.md#IsISBN): Ensures the string is a valid ISBN.
|
||||
- [`lte`](DOC.md#ISLte): Ensures the value is less than or equal to the specified number.
|
||||
- [`luhn`](DOC.md#IsLUHN): Ensures the string is a valid LUHN number.
|
||||
- [`mac`](DOC.md#IsMAC): Ensures the string is a valid MAC address.
|
||||
- [`max-len`](DOC.md#func-maxlen): Ensures the length of the given value (string, slice, or map) is at most n.
|
||||
|
|
|
|||
67
gte.go
Normal file
67
gte.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright (c) 2023-2024 Onur Cinar.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// https://github.com/cinar/checker
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
// nameGte is the name of the greater than or equal to check.
|
||||
nameGte = "gte"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrGte indicates that the value is not greater than or equal to the given value.
|
||||
ErrGte = NewCheckError("NOT_GTE")
|
||||
)
|
||||
|
||||
// IsGte checks if the value is greater than or equal to the given value.
|
||||
func IsGte[T cmp.Ordered](value, n T) (T, error) {
|
||||
if cmp.Compare(value, n) < 0 {
|
||||
return value, newGteError(n)
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// makeGte creates a greater than or equal to check function from a string parameter.
|
||||
// Panics if the parameter cannot be parsed as a number.
|
||||
func makeGte(params string) CheckFunc[reflect.Value] {
|
||||
n, err := strconv.ParseFloat(params, 64)
|
||||
if err != nil {
|
||||
panic("unable to parse params as float")
|
||||
}
|
||||
|
||||
return func(value reflect.Value) (reflect.Value, error) {
|
||||
v := reflect.Indirect(value)
|
||||
|
||||
switch {
|
||||
case v.CanInt():
|
||||
_, err := IsGte(float64(v.Int()), n)
|
||||
return v, err
|
||||
|
||||
case v.CanFloat():
|
||||
_, err := IsGte(v.Float(), n)
|
||||
return v, err
|
||||
|
||||
default:
|
||||
panic("value is not numeric")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// newGteError creates a new greater than or equal to error with the given value.
|
||||
func newGteError[T cmp.Ordered](n T) error {
|
||||
return NewCheckErrorWithData(
|
||||
ErrGte.Code,
|
||||
map[string]interface{}{
|
||||
"n": n,
|
||||
},
|
||||
)
|
||||
}
|
||||
139
gte_test.go
Normal file
139
gte_test.go
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
// Copyright (c) 2023-2024 Onur Cinar.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// https://github.com/cinar/checker
|
||||
|
||||
package v2_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
v2 "github.com/cinar/checker/v2"
|
||||
)
|
||||
|
||||
func TestGteIntSuccess(t *testing.T) {
|
||||
value := 4
|
||||
|
||||
result, err := v2.IsGte(value, 4)
|
||||
if result != value {
|
||||
t.Fatalf("result (%d) is not the original value (%d)", result, value)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGteIntError(t *testing.T) {
|
||||
value := 4
|
||||
|
||||
result, err := v2.IsGte(value, 5)
|
||||
if result != value {
|
||||
t.Fatalf("result (%d) is not the original value (%d)", result, value)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
|
||||
message := "Value cannot be less than 5."
|
||||
|
||||
if err.Error() != message {
|
||||
t.Fatalf("expected %s actual %s", message, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestReflectGteIntError(t *testing.T) {
|
||||
type Person struct {
|
||||
Age int `checkers:"gte:18"`
|
||||
}
|
||||
|
||||
person := &Person{
|
||||
Age: 16,
|
||||
}
|
||||
|
||||
errs, ok := v2.CheckStruct(person)
|
||||
if ok {
|
||||
t.Fatalf("expected errors")
|
||||
}
|
||||
|
||||
if !errors.Is(errs["Age"], v2.ErrGte) {
|
||||
t.Fatalf("expected ErrGte")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReflectGteIntInvalidGte(t *testing.T) {
|
||||
defer FailIfNoPanic(t, "expected panic")
|
||||
|
||||
type Person struct {
|
||||
Age int `checkers:"gte:abcd"`
|
||||
}
|
||||
|
||||
person := &Person{
|
||||
Age: 16,
|
||||
}
|
||||
|
||||
v2.CheckStruct(person)
|
||||
}
|
||||
|
||||
func TestReflectGteIntInvalidType(t *testing.T) {
|
||||
defer FailIfNoPanic(t, "expected panic")
|
||||
|
||||
type Person struct {
|
||||
Age string `checkers:"gte:18"`
|
||||
}
|
||||
|
||||
person := &Person{
|
||||
Age: "18",
|
||||
}
|
||||
|
||||
v2.CheckStruct(person)
|
||||
}
|
||||
|
||||
func TestReflectGteFloatError(t *testing.T) {
|
||||
type Person struct {
|
||||
Weight float64 `checkers:"gte:165.0"`
|
||||
}
|
||||
|
||||
person := &Person{
|
||||
Weight: 150,
|
||||
}
|
||||
|
||||
errs, ok := v2.CheckStruct(person)
|
||||
if ok {
|
||||
t.Fatalf("expected errors")
|
||||
}
|
||||
|
||||
if !errors.Is(errs["Weight"], v2.ErrGte) {
|
||||
t.Fatalf("expected ErrGte")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReflectGteFloatInvalidGte(t *testing.T) {
|
||||
defer FailIfNoPanic(t, "expected panic")
|
||||
|
||||
type Person struct {
|
||||
Weight float64 `checkers:"gte:abcd"`
|
||||
}
|
||||
|
||||
person := &Person{
|
||||
Weight: 170,
|
||||
}
|
||||
|
||||
v2.CheckStruct(person)
|
||||
}
|
||||
|
||||
func TestReflectGteFloatInvalidType(t *testing.T) {
|
||||
defer FailIfNoPanic(t, "expected panic")
|
||||
|
||||
type Person struct {
|
||||
Weight string `checkers:"gte:165.0"`
|
||||
}
|
||||
|
||||
person := &Person{
|
||||
Weight: "170",
|
||||
}
|
||||
|
||||
v2.CheckStruct(person)
|
||||
}
|
||||
|
|
@ -40,11 +40,13 @@ var EnUSMessages = map[string]string{
|
|||
"NOT_DIGITS": "Can only contain digits.",
|
||||
"NOT_EMAIL": "Not a valid email address.",
|
||||
"NOT_FQDN": "Not a fully qualified domain name (FQDN).",
|
||||
"NOT_GTE": "Value cannot be less than {{ .n }}.",
|
||||
"NOT_HEX": "Can only contain hexadecimal characters.",
|
||||
"NOT_IP": "Not a valid IP address.",
|
||||
"NOT_IPV4": "Not a valid IPv4 address.",
|
||||
"NOT_IPV6": "Not a valid IPv6 address.",
|
||||
"NOT_ISBN": "Not a valid ISBN number.",
|
||||
"NOT_LTE": "Value cannot be less than {{ .n }}.",
|
||||
"NOT_LUHN": "Not a valid LUHN number.",
|
||||
"NOT_MAC": "Not a valid MAC address.",
|
||||
"NOT_MAX_LEN": "Value cannot be greater than {{ .max }}.",
|
||||
|
|
|
|||
|
|
@ -14,11 +14,13 @@ var EnUSMessages = map[string]string{
|
|||
"NOT_DIGITS": "Can only contain digits.",
|
||||
"NOT_EMAIL": "Not a valid email address.",
|
||||
"NOT_FQDN": "Not a fully qualified domain name (FQDN).",
|
||||
"NOT_GTE": "Value cannot be less than {{ .n }}.",
|
||||
"NOT_HEX": "Can only contain hexadecimal characters.",
|
||||
"NOT_IP": "Not a valid IP address.",
|
||||
"NOT_IPV4": "Not a valid IPv4 address.",
|
||||
"NOT_IPV6": "Not a valid IPv6 address.",
|
||||
"NOT_ISBN": "Not a valid ISBN number.",
|
||||
"NOT_LTE": "Value cannot be less than {{ .n }}.",
|
||||
"NOT_LUHN": "Not a valid LUHN number.",
|
||||
"NOT_MAC": "Not a valid MAC address.",
|
||||
"NOT_MAX_LEN": "Value cannot be greater than {{ .max }}.",
|
||||
|
|
|
|||
67
lte.go
Normal file
67
lte.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright (c) 2023-2024 Onur Cinar.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// https://github.com/cinar/checker
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
// nameLte is the name of the less than or equal to check.
|
||||
nameLte = "lte"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrLte indicates that the value is not less than or equal to the given value.
|
||||
ErrLte = NewCheckError("NOT_LTE")
|
||||
)
|
||||
|
||||
// IsLte checks if the value is less than or equal to the given value.
|
||||
func IsLte[T cmp.Ordered](value, n T) (T, error) {
|
||||
if cmp.Compare(value, n) > 0 {
|
||||
return value, newLteError(n)
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// makeLte creates a less than or equal to check function from a string parameter.
|
||||
// Panics if the parameter cannot be parsed as a number.
|
||||
func makeLte(params string) CheckFunc[reflect.Value] {
|
||||
n, err := strconv.ParseFloat(params, 64)
|
||||
if err != nil {
|
||||
panic("unable to parse params as float")
|
||||
}
|
||||
|
||||
return func(value reflect.Value) (reflect.Value, error) {
|
||||
v := reflect.Indirect(value)
|
||||
|
||||
switch {
|
||||
case v.CanInt():
|
||||
_, err := IsLte(float64(v.Int()), n)
|
||||
return v, err
|
||||
|
||||
case v.CanFloat():
|
||||
_, err := IsLte(v.Float(), n)
|
||||
return v, err
|
||||
|
||||
default:
|
||||
panic("value is not numeric")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// newLteError creates a new less than or equal to error with the given value.
|
||||
func newLteError[T cmp.Ordered](n T) error {
|
||||
return NewCheckErrorWithData(
|
||||
ErrLte.Code,
|
||||
map[string]interface{}{
|
||||
"n": n,
|
||||
},
|
||||
)
|
||||
}
|
||||
139
lte_test.go
Normal file
139
lte_test.go
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
// Copyright (c) 2023-2024 Onur Cinar.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// https://github.com/cinar/checker
|
||||
|
||||
package v2_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
v2 "github.com/cinar/checker/v2"
|
||||
)
|
||||
|
||||
func TestLteIntSuccess(t *testing.T) {
|
||||
value := 4
|
||||
|
||||
result, err := v2.IsLte(value, 4)
|
||||
if result != value {
|
||||
t.Fatalf("result (%d) is not the original value (%d)", result, value)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLteIntError(t *testing.T) {
|
||||
value := 6
|
||||
|
||||
result, err := v2.IsLte(value, 5)
|
||||
if result != value {
|
||||
t.Fatalf("result (%d) is not the original value (%d)", result, value)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
|
||||
message := "Value cannot be less than 5."
|
||||
|
||||
if err.Error() != message {
|
||||
t.Fatalf("expected %s actual %s", message, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestReflectLteIntError(t *testing.T) {
|
||||
type Person struct {
|
||||
Age int `checkers:"lte:18"`
|
||||
}
|
||||
|
||||
person := &Person{
|
||||
Age: 21,
|
||||
}
|
||||
|
||||
errs, ok := v2.CheckStruct(person)
|
||||
if ok {
|
||||
t.Fatalf("expected errors")
|
||||
}
|
||||
|
||||
if !errors.Is(errs["Age"], v2.ErrLte) {
|
||||
t.Fatalf("expected ErrLte")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReflectLteIntInvalidLte(t *testing.T) {
|
||||
defer FailIfNoPanic(t, "expected panic")
|
||||
|
||||
type Person struct {
|
||||
Age int `checkers:"lte:abcd"`
|
||||
}
|
||||
|
||||
person := &Person{
|
||||
Age: 16,
|
||||
}
|
||||
|
||||
v2.CheckStruct(person)
|
||||
}
|
||||
|
||||
func TestReflectLteIntInvalidType(t *testing.T) {
|
||||
defer FailIfNoPanic(t, "expected panic")
|
||||
|
||||
type Person struct {
|
||||
Age string `checkers:"lte:18"`
|
||||
}
|
||||
|
||||
person := &Person{
|
||||
Age: "18",
|
||||
}
|
||||
|
||||
v2.CheckStruct(person)
|
||||
}
|
||||
|
||||
func TestReflectLteFloatError(t *testing.T) {
|
||||
type Person struct {
|
||||
Weight float64 `checkers:"lte:165.0"`
|
||||
}
|
||||
|
||||
person := &Person{
|
||||
Weight: 170,
|
||||
}
|
||||
|
||||
errs, ok := v2.CheckStruct(person)
|
||||
if ok {
|
||||
t.Fatalf("expected errors")
|
||||
}
|
||||
|
||||
if !errors.Is(errs["Weight"], v2.ErrLte) {
|
||||
t.Fatalf("expected ErrLte")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReflectLteFloatInvalidLte(t *testing.T) {
|
||||
defer FailIfNoPanic(t, "expected panic")
|
||||
|
||||
type Person struct {
|
||||
Weight float64 `checkers:"lte:abcd"`
|
||||
}
|
||||
|
||||
person := &Person{
|
||||
Weight: 170,
|
||||
}
|
||||
|
||||
v2.CheckStruct(person)
|
||||
}
|
||||
|
||||
func TestReflectLteFloatInvalidType(t *testing.T) {
|
||||
defer FailIfNoPanic(t, "expected panic")
|
||||
|
||||
type Person struct {
|
||||
Weight string `checkers:"lte:165.0"`
|
||||
}
|
||||
|
||||
person := &Person{
|
||||
Weight: "170",
|
||||
}
|
||||
|
||||
v2.CheckStruct(person)
|
||||
}
|
||||
2
maker.go
2
maker.go
|
|
@ -23,6 +23,7 @@ var makers = map[string]MakeCheckFunc{
|
|||
nameDigits: makeDigits,
|
||||
nameEmail: makeEmail,
|
||||
nameFQDN: makeFQDN,
|
||||
nameGte: makeGte,
|
||||
nameHex: makeHex,
|
||||
nameHTMLEscape: makeHTMLEscape,
|
||||
nameHTMLUnescape: makeHTMLUnescape,
|
||||
|
|
@ -31,6 +32,7 @@ var makers = map[string]MakeCheckFunc{
|
|||
nameIPv6: makeIPv6,
|
||||
nameISBN: makeISBN,
|
||||
nameLower: makeLower,
|
||||
nameLte: makeLte,
|
||||
nameLUHN: makeLUHN,
|
||||
nameMAC: makeMAC,
|
||||
nameMaxLen: makeMaxLen,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue