Skip to content

Commit

Permalink
Gte and Lte checks are added. (#161)
Browse files Browse the repository at this point in the history
# Describe Request

Greater than or equal tp (Gte) and less than or equal to (Lte) checks
are added.

# Change Type

New checks.
  • Loading branch information
cinar authored Dec 31, 2024
1 parent 21065d3 commit 1188cff
Show file tree
Hide file tree
Showing 9 changed files with 460 additions and 2 deletions.
40 changes: 39 additions & 1 deletion DOC.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ 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>)
- [func IsIPv6\(value string\) \(string, error\)](<#IsIPv6>)
- [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>)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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>)

Expand Down Expand Up @@ -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>)

Expand Down Expand Up @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
67 changes: 67 additions & 0 deletions gte.go
Original file line number Diff line number Diff line change
@@ -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 changes: 139 additions & 0 deletions gte_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
2 changes: 2 additions & 0 deletions locales/DOC.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}.",
Expand Down
2 changes: 2 additions & 0 deletions locales/en_us.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}.",
Expand Down
Loading

0 comments on commit 1188cff

Please sign in to comment.