Skip to content

Commit 1188cff

Browse files
authored
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.
1 parent 21065d3 commit 1188cff

File tree

9 files changed

+460
-2
lines changed

9 files changed

+460
-2
lines changed

DOC.md

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@ Package v2 Checker is a Go library for validating user input through checker rul
2929
- [func IsDiscoverCreditCard\(number string\) \(string, error\)](<#IsDiscoverCreditCard>)
3030
- [func IsEmail\(value string\) \(string, error\)](<#IsEmail>)
3131
- [func IsFQDN\(value string\) \(string, error\)](<#IsFQDN>)
32+
- [func IsGte\[T cmp.Ordered\]\(value, n T\) \(T, error\)](<#IsGte>)
3233
- [func IsHex\(value string\) \(string, error\)](<#IsHex>)
3334
- [func IsIP\(value string\) \(string, error\)](<#IsIP>)
3435
- [func IsIPv4\(value string\) \(string, error\)](<#IsIPv4>)
3536
- [func IsIPv6\(value string\) \(string, error\)](<#IsIPv6>)
3637
- [func IsISBN\(value string\) \(string, error\)](<#IsISBN>)
3738
- [func IsJcbCreditCard\(number string\) \(string, error\)](<#IsJcbCreditCard>)
3839
- [func IsLUHN\(value string\) \(string, error\)](<#IsLUHN>)
40+
- [func IsLte\[T cmp.Ordered\]\(value, n T\) \(T, error\)](<#IsLte>)
3941
- [func IsMAC\(value string\) \(string, error\)](<#IsMAC>)
4042
- [func IsMasterCardCreditCard\(number string\) \(string, error\)](<#IsMasterCardCreditCard>)
4143
- [func IsRegexp\(expression, value string\) \(string, error\)](<#IsRegexp>)
@@ -80,6 +82,24 @@ const (
8082

8183
## Variables
8284

85+
<a name="ErrGte"></a>
86+
87+
```go
88+
var (
89+
// ErrGte indicates that the value is not greater than or equal to the given value.
90+
ErrGte = NewCheckError("NOT_GTE")
91+
)
92+
```
93+
94+
<a name="ErrLte"></a>
95+
96+
```go
97+
var (
98+
// ErrLte indicates that the value is not less than or equal to the given value.
99+
ErrLte = NewCheckError("NOT_LTE")
100+
)
101+
```
102+
83103
<a name="ErrMaxLen"></a>
84104

85105
```go
@@ -707,6 +727,15 @@ func main() {
707727
</p>
708728
</details>
709729

730+
<a name="IsGte"></a>
731+
## func [IsGte](<https://github.com/cinar/checker/blob/main/gte.go#L25>)
732+
733+
```go
734+
func IsGte[T cmp.Ordered](value, n T) (T, error)
735+
```
736+
737+
IsGte checks if the value is greater than or equal to the given value.
738+
710739
<a name="IsHex"></a>
711740
## func [IsHex](<https://github.com/cinar/checker/blob/main/hex.go#L23>)
712741

@@ -944,6 +973,15 @@ func main() {
944973
</p>
945974
</details>
946975

976+
<a name="IsLte"></a>
977+
## func [IsLte](<https://github.com/cinar/checker/blob/main/lte.go#L25>)
978+
979+
```go
980+
func IsLte[T cmp.Ordered](value, n T) (T, error)
981+
```
982+
983+
IsLte checks if the value is less than or equal to the given value.
984+
947985
<a name="IsMAC"></a>
948986
## func [IsMAC](<https://github.com/cinar/checker/blob/main/mac.go#L24>)
949987

@@ -1173,7 +1211,7 @@ func RegisterLocale(locale string, messages map[string]string)
11731211
RegisterLocale registers the localized error messages for the given locale.
11741212

11751213
<a name="RegisterMaker"></a>
1176-
## func [RegisterMaker](<https://github.com/cinar/checker/blob/main/maker.go#L51>)
1214+
## func [RegisterMaker](<https://github.com/cinar/checker/blob/main/maker.go#L53>)
11771215

11781216
```go
11791217
func RegisterMaker(name string, maker MakeCheckFunc)

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,13 @@ type Person struct {
104104
- [`digits`](DOC.md#IsDigits): Ensures the string contains only digits.
105105
- [`email`](DOC.md#IsEmail): Ensures the string is a valid email address.
106106
- [`fqdn`](DOC.md#IsFQDN): Ensures the string is a valid fully qualified domain name.
107-
- [`hex`](DOC.md#IsHex): Ensures the string contains only hex digits.
107+
- [`gte`](DOC.md#IsGte): Ensures the value is greater than or equal to the specified number.
108+
- [`hex`](DOC.md#IsHex): Ensures the string contains only hexadecimal digits.
108109
- [`ip`](DOC.md#IsIP): Ensures the string is a valid IP address.
109110
- [`ipv4`](DOC.md#IsIPv4): Ensures the string is a valid IPv4 address.
110111
- [`ipv6`](DOC.md#IsIPv6): Ensures the string is a valid IPv6 address.
111112
- [`isbn`](DOC.md#IsISBN): Ensures the string is a valid ISBN.
113+
- [`lte`](DOC.md#ISLte): Ensures the value is less than or equal to the specified number.
112114
- [`luhn`](DOC.md#IsLUHN): Ensures the string is a valid LUHN number.
113115
- [`mac`](DOC.md#IsMAC): Ensures the string is a valid MAC address.
114116
- [`max-len`](DOC.md#func-maxlen): Ensures the length of the given value (string, slice, or map) is at most n.

gte.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright (c) 2023-2024 Onur Cinar.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
// https://github.com/cinar/checker
5+
6+
package v2
7+
8+
import (
9+
"cmp"
10+
"reflect"
11+
"strconv"
12+
)
13+
14+
const (
15+
// nameGte is the name of the greater than or equal to check.
16+
nameGte = "gte"
17+
)
18+
19+
var (
20+
// ErrGte indicates that the value is not greater than or equal to the given value.
21+
ErrGte = NewCheckError("NOT_GTE")
22+
)
23+
24+
// IsGte checks if the value is greater than or equal to the given value.
25+
func IsGte[T cmp.Ordered](value, n T) (T, error) {
26+
if cmp.Compare(value, n) < 0 {
27+
return value, newGteError(n)
28+
}
29+
30+
return value, nil
31+
}
32+
33+
// makeGte creates a greater than or equal to check function from a string parameter.
34+
// Panics if the parameter cannot be parsed as a number.
35+
func makeGte(params string) CheckFunc[reflect.Value] {
36+
n, err := strconv.ParseFloat(params, 64)
37+
if err != nil {
38+
panic("unable to parse params as float")
39+
}
40+
41+
return func(value reflect.Value) (reflect.Value, error) {
42+
v := reflect.Indirect(value)
43+
44+
switch {
45+
case v.CanInt():
46+
_, err := IsGte(float64(v.Int()), n)
47+
return v, err
48+
49+
case v.CanFloat():
50+
_, err := IsGte(v.Float(), n)
51+
return v, err
52+
53+
default:
54+
panic("value is not numeric")
55+
}
56+
}
57+
}
58+
59+
// newGteError creates a new greater than or equal to error with the given value.
60+
func newGteError[T cmp.Ordered](n T) error {
61+
return NewCheckErrorWithData(
62+
ErrGte.Code,
63+
map[string]interface{}{
64+
"n": n,
65+
},
66+
)
67+
}

gte_test.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Copyright (c) 2023-2024 Onur Cinar.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
// https://github.com/cinar/checker
5+
6+
package v2_test
7+
8+
import (
9+
"errors"
10+
"testing"
11+
12+
v2 "github.com/cinar/checker/v2"
13+
)
14+
15+
func TestGteIntSuccess(t *testing.T) {
16+
value := 4
17+
18+
result, err := v2.IsGte(value, 4)
19+
if result != value {
20+
t.Fatalf("result (%d) is not the original value (%d)", result, value)
21+
}
22+
23+
if err != nil {
24+
t.Fatal(err)
25+
}
26+
}
27+
28+
func TestGteIntError(t *testing.T) {
29+
value := 4
30+
31+
result, err := v2.IsGte(value, 5)
32+
if result != value {
33+
t.Fatalf("result (%d) is not the original value (%d)", result, value)
34+
}
35+
36+
if err == nil {
37+
t.Fatal("expected error")
38+
}
39+
40+
message := "Value cannot be less than 5."
41+
42+
if err.Error() != message {
43+
t.Fatalf("expected %s actual %s", message, err.Error())
44+
}
45+
}
46+
47+
func TestReflectGteIntError(t *testing.T) {
48+
type Person struct {
49+
Age int `checkers:"gte:18"`
50+
}
51+
52+
person := &Person{
53+
Age: 16,
54+
}
55+
56+
errs, ok := v2.CheckStruct(person)
57+
if ok {
58+
t.Fatalf("expected errors")
59+
}
60+
61+
if !errors.Is(errs["Age"], v2.ErrGte) {
62+
t.Fatalf("expected ErrGte")
63+
}
64+
}
65+
66+
func TestReflectGteIntInvalidGte(t *testing.T) {
67+
defer FailIfNoPanic(t, "expected panic")
68+
69+
type Person struct {
70+
Age int `checkers:"gte:abcd"`
71+
}
72+
73+
person := &Person{
74+
Age: 16,
75+
}
76+
77+
v2.CheckStruct(person)
78+
}
79+
80+
func TestReflectGteIntInvalidType(t *testing.T) {
81+
defer FailIfNoPanic(t, "expected panic")
82+
83+
type Person struct {
84+
Age string `checkers:"gte:18"`
85+
}
86+
87+
person := &Person{
88+
Age: "18",
89+
}
90+
91+
v2.CheckStruct(person)
92+
}
93+
94+
func TestReflectGteFloatError(t *testing.T) {
95+
type Person struct {
96+
Weight float64 `checkers:"gte:165.0"`
97+
}
98+
99+
person := &Person{
100+
Weight: 150,
101+
}
102+
103+
errs, ok := v2.CheckStruct(person)
104+
if ok {
105+
t.Fatalf("expected errors")
106+
}
107+
108+
if !errors.Is(errs["Weight"], v2.ErrGte) {
109+
t.Fatalf("expected ErrGte")
110+
}
111+
}
112+
113+
func TestReflectGteFloatInvalidGte(t *testing.T) {
114+
defer FailIfNoPanic(t, "expected panic")
115+
116+
type Person struct {
117+
Weight float64 `checkers:"gte:abcd"`
118+
}
119+
120+
person := &Person{
121+
Weight: 170,
122+
}
123+
124+
v2.CheckStruct(person)
125+
}
126+
127+
func TestReflectGteFloatInvalidType(t *testing.T) {
128+
defer FailIfNoPanic(t, "expected panic")
129+
130+
type Person struct {
131+
Weight string `checkers:"gte:165.0"`
132+
}
133+
134+
person := &Person{
135+
Weight: "170",
136+
}
137+
138+
v2.CheckStruct(person)
139+
}

locales/DOC.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,13 @@ var EnUSMessages = map[string]string{
4040
"NOT_DIGITS": "Can only contain digits.",
4141
"NOT_EMAIL": "Not a valid email address.",
4242
"NOT_FQDN": "Not a fully qualified domain name (FQDN).",
43+
"NOT_GTE": "Value cannot be less than {{ .n }}.",
4344
"NOT_HEX": "Can only contain hexadecimal characters.",
4445
"NOT_IP": "Not a valid IP address.",
4546
"NOT_IPV4": "Not a valid IPv4 address.",
4647
"NOT_IPV6": "Not a valid IPv6 address.",
4748
"NOT_ISBN": "Not a valid ISBN number.",
49+
"NOT_LTE": "Value cannot be less than {{ .n }}.",
4850
"NOT_LUHN": "Not a valid LUHN number.",
4951
"NOT_MAC": "Not a valid MAC address.",
5052
"NOT_MAX_LEN": "Value cannot be greater than {{ .max }}.",

locales/en_us.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ var EnUSMessages = map[string]string{
1414
"NOT_DIGITS": "Can only contain digits.",
1515
"NOT_EMAIL": "Not a valid email address.",
1616
"NOT_FQDN": "Not a fully qualified domain name (FQDN).",
17+
"NOT_GTE": "Value cannot be less than {{ .n }}.",
1718
"NOT_HEX": "Can only contain hexadecimal characters.",
1819
"NOT_IP": "Not a valid IP address.",
1920
"NOT_IPV4": "Not a valid IPv4 address.",
2021
"NOT_IPV6": "Not a valid IPv6 address.",
2122
"NOT_ISBN": "Not a valid ISBN number.",
23+
"NOT_LTE": "Value cannot be less than {{ .n }}.",
2224
"NOT_LUHN": "Not a valid LUHN number.",
2325
"NOT_MAC": "Not a valid MAC address.",
2426
"NOT_MAX_LEN": "Value cannot be greater than {{ .max }}.",

0 commit comments

Comments
 (0)