Skip to content

Commit 5f1956d

Browse files
authored
Merge pull request #6 from Tavernari/add-negatable-predicate
2 parents 7563eb7 + 9a788b1 commit 5f1956d

6 files changed

Lines changed: 144 additions & 0 deletions

File tree

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ print(result4) // true
3636
// Check if text contains "fox" AND ("Jumps" OR "swift") case insensitively and without diacritics
3737
let result5 = text.contains(~"fox" && (~"Jumps" || ~"swift"))
3838
print(result5) // true
39+
40+
// Check if text does NOT contain "cat" AND "bird"
41+
let result6 = text.contains(!"cat" && !"bird")
42+
print(result6) // true
43+
44+
// Check if text does NOT contain "brown"
45+
let result7 = text.contains(!"brown")
46+
print(result7) // false
47+
48+
// Check if text does NOT contain "cat" case insensitively and without diacritics
49+
let result8 = text.contains(!~"cat")
50+
print(result8) // true
3951
```
4052

4153
## How to Install
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//
2+
// NegatablePredicateSearchStrategy.swift
3+
//
4+
//
5+
// Created by Victor C Tavernari on 24/03/2023.
6+
//
7+
8+
import Foundation
9+
10+
/// A search strategy that negates the result of another search strategy.
11+
final class NegatablePredicateSearchStrategy: SearchStrategy {
12+
13+
let searchStrategy: SearchStrategy
14+
15+
init(searchStrategy: SearchStrategy) {
16+
17+
self.searchStrategy = searchStrategy
18+
}
19+
20+
/// Evaluates the given string with the negated search strategy.
21+
///
22+
/// - Parameter string: The string to be evaluated.
23+
/// - Returns: `true` if the given string does not match the original search strategy, `false` otherwise.
24+
func evaluate(string: String) -> Bool {
25+
26+
return !self.searchStrategy.evaluate(string: string)
27+
}
28+
}
29+
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//
2+
// NegatableValueSearchStrategy.swift
3+
//
4+
//
5+
// Created by Victor C Tavernari on 24/03/2023.
6+
//
7+
8+
import Foundation
9+
10+
/// A search strategy that negates the presence of a given value in a string.
11+
final class NegatableValueSearchStrategy: SearchStrategy {
12+
13+
let value: String
14+
15+
init(value: String) {
16+
17+
self.value = value
18+
}
19+
20+
/// Evaluates the given string with the negated value search strategy.
21+
///
22+
/// - Parameter string: The string to be evaluated.
23+
/// - Returns: `true` if the given string does not contain the value, `false` otherwise.
24+
func evaluate(string: String) -> Bool {
25+
26+
return !string.contains(self.value)
27+
}
28+
}
29+

Sources/StringContainsOperators/SearchStrategyMaker.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ enum SearchStrategyMaker {
3838

3939
case let .diacriticAndCaseInsensitive(value):
4040
return DiacriticAndCaseInsensitiveSearchStrategy(value: value)
41+
42+
case let .negatable(value):
43+
return NegatableValueSearchStrategy(value: value)
44+
45+
case let .negatablePredicate(predicate):
46+
return NegatablePredicateSearchStrategy(searchStrategy: self.make(predicate: predicate))
4147
}
4248
}
4349
}

Sources/StringContainsOperators/StringContainsOperators.swift

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Foundation
99
infix operator || : LogicalDisjunctionPrecedence
1010
infix operator && : LogicalConjunctionPrecedence
1111
prefix operator ~
12+
prefix operator !
1213

1314
/// An enum representing a string search predicate.
1415
public indirect enum StringPredicate {
@@ -33,62 +34,118 @@ public indirect enum StringPredicate {
3334

3435
/// Represents a case-insensitive and diacritic-insensitive search for a given string.
3536
case diacriticAndCaseInsensitive(String)
37+
38+
case negatable(String)
39+
40+
case negatablePredicate(StringPredicate)
3641
}
3742

3843
/// Returns a `StringPredicate` that performs a logical OR operation between two strings.
44+
/// - Parameters:
45+
/// - lhs: The first string to be evaluated.
46+
/// - rhs: The second string to be evaluated.
47+
/// - Returns: A `StringPredicate` that performs a logical OR operation between two strings.
3948
public func || (lhs: String, rhs: String) -> StringPredicate {
4049

4150
return .or([lhs, rhs])
4251
}
4352

4453
/// Returns a `StringPredicate` that performs a logical OR operation between a string and a `StringPredicate`.
54+
/// - Parameters:
55+
/// - lhs: The string to be evaluated.
56+
/// - rhs: The `StringPredicate` to be evaluated.
57+
/// - Returns: A `StringPredicate` that performs a logical OR operation between a string and a `StringPredicate`.
4558
public func || (lhs: String, rhs: StringPredicate) -> StringPredicate {
4659

4760
return .orPredicates(lhs, rhs)
4861
}
4962

5063
/// Returns a `StringPredicate` that performs a logical OR operation between a `StringPredicate` and a string.
64+
/// - Parameters:
65+
/// - lhs: The `StringPredicate` to be evaluated.
66+
/// - rhs: The string to be evaluated.
67+
/// - Returns: A `StringPredicate` that performs a logical OR operation between a `StringPredicate` and a string.
5168
public func || (lhs: StringPredicate, rhs: String) -> StringPredicate {
5269

5370
return .orPredicates(rhs, lhs)
5471
}
5572

5673
/// Returns a `StringPredicate` that performs a logical OR operation between two `StringPredicate`s.
74+
/// - Parameters:
75+
/// - lhs: The first `StringPredicate` to be evaluated.
76+
/// - rhs: The second `StringPredicate` to be evaluated.
77+
/// - Returns: A `StringPredicate` that performs a logical OR operation between two `StringPredicate`s.
5778
public func || (lhs: StringPredicate, rhs: StringPredicate) -> StringPredicate {
5879

5980
return .orOnlyPredicates([rhs, lhs])
6081
}
6182

6283
/// Returns a `StringPredicate` that performs a logical AND operation between two strings.
84+
/// - Parameters:
85+
/// - lhs: The first string to be evaluated.
86+
/// - rhs: The second string to be evaluated.
87+
/// - Returns: A `StringPredicate` that performs a logical AND operation between two strings.
6388
public func && (lhs: String, rhs: String) -> StringPredicate {
6489

6590
return .and([lhs, rhs])
6691
}
6792

6893
/// Returns a `StringPredicate` that performs a logical AND operation between a string and a `StringPredicate`.
94+
/// - Parameters:
95+
/// - lhs: The string to be evaluated.
96+
/// - rhs: The `StringPredicate` to be evaluated.
97+
/// - Returns: A `StringPredicate` that performs a logical AND operation between a string and a `StringPredicate`.
6998
public func && (lhs: String, rhs: StringPredicate) -> StringPredicate {
7099

71100
return .andPredicates(lhs, rhs)
72101
}
73102

74103
/// Returns a `StringPredicate` that performs a logical AND operation between a `StringPredicate` and a string.
104+
/// - Parameters:
105+
/// - lhs: The `StringPredicate` to be evaluated.
106+
/// - rhs: The string to be evaluated.
107+
/// - Returns: A `StringPredicate` that performs a logical AND operation between a `StringPredicate` and a string.
75108
public func && (lhs: StringPredicate, rhs: String) -> StringPredicate {
76109

77110
return .andPredicates(rhs, lhs)
78111
}
79112

80113
/// Returns a `StringPredicate` that performs a logical AND operation between two `StringPredicate`s.
114+
/// - Parameters:
115+
/// - lhs: The first `StringPredicate` to be evaluated.
116+
/// - rhs: The second `StringPredicate` to be evaluated.
117+
/// - Returns: A `StringPredicate
81118
public func && (lhs: StringPredicate, rhs: StringPredicate) -> StringPredicate {
82119

83120
return .andOnlyPredicates([rhs, lhs])
84121
}
85122

86123
/// Returns a `StringPredicate` that performs a case-insensitive and diacritic-insensitive search for a given string.
124+
///
125+
/// - Parameter value: The value to be evaluated.
126+
/// - Returns: A `StringPredicate` that performs a case-insensitive and diacritic-insensitive search for a given string.
87127
public prefix func ~ (value: String) -> StringPredicate {
88128

89129
return .diacriticAndCaseInsensitive(value)
90130
}
91131

132+
/// Returns a `StringPredicate` that negates another `StringPredicate`.
133+
///
134+
/// - Parameter predicate: The predicate to be negated.
135+
/// - Returns: A `StringPredicate` that represents the negation of the given predicate.
136+
public prefix func ! (predicate: StringPredicate) -> StringPredicate {
137+
138+
return .negatablePredicate(predicate)
139+
}
140+
141+
/// Returns a `StringPredicate` that negates a given value.
142+
///
143+
/// - Parameter value: The value to be negated.
144+
/// - Returns: A `StringPredicate` that represents the negation of the given value.
145+
public prefix func ! (value: String) -> StringPredicate {
146+
147+
return .negatable(value)
148+
}
92149

93150
public extension String {
94151

Tests/StringContainsOperatorsTests/StringContainsOperatorsTests.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,15 @@ final class StringContainsOperatorsTests: XCTestCase {
102102
XCTAssertTrue("wORLD-".contains(predicate))
103103
XCTAssertFalse("Goodbye".contains(predicate))
104104
}
105+
106+
func testNegatablePredicate() {
107+
let text = "Hello my little friend"
108+
109+
XCTAssertTrue(text.contains(!"fiance"))
110+
XCTAssertFalse(text.contains(!"my"))
111+
XCTAssertTrue(text.contains(!("enemy" && "little")))
112+
XCTAssertFalse(text.contains(!("friend" && "little")))
113+
XCTAssertTrue(text.contains(!("enemy" || "big")))
114+
XCTAssertFalse(text.contains(!("friend" || "big")))
115+
}
105116
}

0 commit comments

Comments
 (0)