Skip to content

Commit

Permalink
Add functional predicate (#171)
Browse files Browse the repository at this point in the history
Enable the execution of assertion functions in a functional manner.
  • Loading branch information
donutloop authored Feb 20, 2024
1 parent 874d09f commit 3cd9d6b
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 0 deletions.
34 changes: 34 additions & 0 deletions function/predicate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package function

// And returns a composed predicate that represents the logical AND of a list of predicates.
// It evaluates to true only if all predicates evaluate to true for the given value.
func And[T any](predicates ...func(T) bool) func(T) bool {
return func(value T) bool {
for _, predicate := range predicates {
if !predicate(value) {
return false // Short-circuit on the first false predicate
}
}
return true // True if all predicates are true
}
}

// Negate returns a predicate that represents the logical negation of this predicate.
func Negate[T any](predicate func(T) bool) func(T) bool {
return func(value T) bool {
return !predicate(value)
}
}

// Or returns a composed predicate that represents the logical OR of a list of predicates.
// It evaluates to true if at least one of the predicates evaluates to true for the given value.
func Or[T any](predicates ...func(T) bool) func(T) bool {
return func(value T) bool {
for _, predicate := range predicates {
if predicate(value) {
return true // Short-circuit on the first true predicate
}
}
return false // False if all predicates are false
}
}
75 changes: 75 additions & 0 deletions function/predicate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package function

import (
"strings"
"testing"

"github.com/duke-git/lancet/v2/internal"
)

func TestPredicatesNegatePure(t *testing.T) {
t.Parallel()

assert := internal.NewAssert(t, "TestPredicatesNegatePure")

// Define some simple predicates for demonstration
isUpperCase := func(s string) bool {
return strings.ToUpper(s) == s
}
isLowerCase := func(s string) bool {
return strings.ToLower(s) == s
}
isMixedCase := Negate(Or(isUpperCase, isLowerCase))

assert.ShouldBeFalse(isMixedCase("ABC"))
assert.ShouldBeTrue(isMixedCase("AbC"))
}

func TestPredicatesOrPure(t *testing.T) {
t.Parallel()

assert := internal.NewAssert(t, "TestPredicatesOrPure")

containsDigitOrSpecialChar := Or(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return strings.ContainsAny(s, "!@#$%") },
)

assert.ShouldBeTrue(containsDigitOrSpecialChar("hello!"))
assert.ShouldBeFalse(containsDigitOrSpecialChar("hello"))
}

func TestPredicatesAndPure(t *testing.T) {
t.Parallel()

assert := internal.NewAssert(t, "TestPredicatesAndPure")

isNumericAndLength5 := And(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)

assert.ShouldBeTrue(isNumericAndLength5("12345"))
assert.ShouldBeFalse(isNumericAndLength5("1234"))
assert.ShouldBeFalse(isNumericAndLength5("abcde"))
}

func TestPredicatesMix(t *testing.T) {
t.Parallel()

assert := internal.NewAssert(t, "TestPredicatesMix")

a := Or(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return strings.ContainsAny(s, "!") },
)

b := And(
func(s string) bool { return strings.ContainsAny(s, "hello") },
func(s string) bool { return strings.ContainsAny(s, "!") },
)

c := Negate(And(a, b))

assert.ShouldBeFalse(c("hello!"))
}

0 comments on commit 3cd9d6b

Please sign in to comment.