Skip to content

Commit cd077fc

Browse files
committed
feat: add comparison validation rule
1 parent 78cc5d9 commit cd077fc

File tree

4 files changed

+143
-0
lines changed

4 files changed

+143
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ struct RegistrationView: View {
309309
| `EmailValidationRule` | Validates email format | `EmailValidationRule(error: "Please enter a valid email")` |
310310
| `CharactersValidationRule` | Validates that a string contains only characters from the allowed CharacterSet | `CharactersValidationRule(characterSet: .letters, error: "Invalid characters")` |
311311
| `NilValidationRule` | Validates that value is nil | `NilValidationRule(error: "Value must be nil")`
312+
| `ComparisonValidationRule` | Validates that input against a comparison constraint | `ComparisonValidationRule(greaterThan: 0, error: "Must be greater than 0")`
312313

313314
## Custom Validators
314315

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
//
2+
// Validator
3+
// Copyright © 2025 Space Code. All rights reserved.
4+
//
5+
6+
/// A validation rule that checks input against a comparison constraint.
7+
///
8+
/// Supports comparisons such as:
9+
/// - `>` (greater than)
10+
/// - `>=` (greater than or equal to)
11+
/// - `<` (less than)
12+
/// - `<=` (less than or equal to)
13+
///
14+
/// # Examples:
15+
/// ```swift
16+
/// let rule1 = ComparisonValidationRule(greaterThan: 0, error: "Must be greater than 0")
17+
/// rule1.validate(input: 5) // true
18+
/// rule1.validate(input: -1) // false
19+
///
20+
/// let rule2 = ComparisonValidationRule(lessThanOrEqual: 100, error: "Must be <= 100")
21+
/// rule2.validate(input: 100) // true
22+
/// rule2.validate(input: 150) // false
23+
/// ```
24+
public struct ComparisonValidationRule<T: Comparable>: IValidationRule {
25+
// MARK: - Types
26+
27+
public typealias Input = T
28+
29+
/// Supported comparison operators.
30+
public enum Condition {
31+
case greaterThan(T)
32+
case greaterThanOrEqual(T)
33+
case lessThan(T)
34+
case lessThanOrEqual(T)
35+
}
36+
37+
// MARK: - Properties
38+
39+
/// The comparison condition to evaluate.
40+
public let condition: Condition
41+
42+
/// The validation error returned when validation fails.
43+
public let error: IValidationError
44+
45+
// MARK: - Initialization
46+
47+
/// Creates a comparison-based validation rule.
48+
///
49+
/// - Parameters:
50+
/// - condition: The comparison condition.
51+
/// - error: The validation error returned if validation fails.
52+
public init(condition: Condition, error: IValidationError) {
53+
self.condition = condition
54+
self.error = error
55+
}
56+
57+
/// Convenience initializer for `greaterThan`.
58+
public init(greaterThan value: T, error: IValidationError) {
59+
self.init(condition: .greaterThan(value), error: error)
60+
}
61+
62+
/// Convenience initializer for `greaterThanOrEqual`.
63+
public init(greaterThanOrEqual value: T, error: IValidationError) {
64+
self.init(condition: .greaterThanOrEqual(value), error: error)
65+
}
66+
67+
/// Convenience initializer for `lessThan`.
68+
public init(lessThan value: T, error: IValidationError) {
69+
self.init(condition: .lessThan(value), error: error)
70+
}
71+
72+
/// Convenience initializer for `lessThanOrEqual`.
73+
public init(lessThanOrEqual value: T, error: IValidationError) {
74+
self.init(condition: .lessThanOrEqual(value), error: error)
75+
}
76+
77+
// MARK: - IValidationRule
78+
79+
public func validate(input: T) -> Bool {
80+
switch condition {
81+
case let .greaterThan(value):
82+
input > value
83+
case let .greaterThanOrEqual(value):
84+
input >= value
85+
case let .lessThan(value):
86+
input < value
87+
case let .lessThanOrEqual(value):
88+
input <= value
89+
}
90+
}
91+
}

Sources/ValidatorCore/Validator.docc/Overview.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ ValidatorCore contains all core validation rules, utilities, and mechanisms for
3131
- ``RegexValidationRule``
3232
- ``SuffixValidationRule``
3333
- ``URLValidationRule``
34+
- ``ComparisonValidationRule``
3435

3536
### Articles
3637

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//
2+
// Validator
3+
// Copyright © 2025 Space Code. All rights reserved.
4+
//
5+
6+
@testable import ValidatorCore
7+
import XCTest
8+
9+
// MARK: - ComparisonValidationRuleTests
10+
11+
final class ComparisonValidationRuleTests: XCTestCase {
12+
func test_greaterThanRule_passesForValidValue() {
13+
let sut = ComparisonValidationRule(greaterThan: 10, error: String.error)
14+
XCTAssertTrue(sut.validate(input: 20))
15+
}
16+
17+
func test_greaterThanRule_failsForInvalidValue() {
18+
let sut = ComparisonValidationRule(greaterThan: 10, error: String.error)
19+
XCTAssertFalse(sut.validate(input: 10))
20+
}
21+
22+
func test_greaterThanOrEqualRule_allowsEqualValue() {
23+
let sut = ComparisonValidationRule(greaterThanOrEqual: 10, error: String.error)
24+
XCTAssertTrue(sut.validate(input: 10))
25+
}
26+
27+
func test_lessThanRule_passesForSmallerValues() {
28+
let sut = ComparisonValidationRule(lessThan: 100, error: String.error)
29+
XCTAssertTrue(sut.validate(input: 50))
30+
XCTAssertFalse(sut.validate(input: 100))
31+
}
32+
33+
func test_lessThanOrEqualRule_allowsEqualValue() {
34+
let sut = ComparisonValidationRule(lessThanOrEqual: 100, error: String.error)
35+
XCTAssertTrue(sut.validate(input: 100))
36+
XCTAssertFalse(sut.validate(input: 101))
37+
}
38+
39+
func test_ruleWorksWithStringComparable() {
40+
let sut = ComparisonValidationRule(lessThan: "m", error: String.error)
41+
XCTAssertTrue(sut.validate(input: "apple"))
42+
XCTAssertFalse(sut.validate(input: "z"))
43+
}
44+
}
45+
46+
// MARK: Constants
47+
48+
private extension String {
49+
static let error = "Invalid characters"
50+
}

0 commit comments

Comments
 (0)