From df85234acf45543fed9dfa72b90effdb27a535d2 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 7 Dec 2025 14:53:39 +0400 Subject: [PATCH] feat: add comparison validation rule --- README.md | 1 + .../Rules/ComparisonValidationRule.swift | 91 +++++++++++++++++++ .../ValidatorCore/Validator.docc/Overview.md | 1 + .../Rules/ComparisonValidationRuleTests.swift | 50 ++++++++++ 4 files changed, 143 insertions(+) create mode 100644 Sources/ValidatorCore/Classes/Rules/ComparisonValidationRule.swift create mode 100644 Tests/ValidatorCoreTests/UnitTests/Rules/ComparisonValidationRuleTests.swift diff --git a/README.md b/README.md index 0a4f56d..0b6575c 100644 --- a/README.md +++ b/README.md @@ -313,6 +313,7 @@ struct RegistrationView: View { | `NoWhitespaceValidationRule` | Validates that a string does not contain any whitespace characters | `NoWhitespaceValidationRule(error: "Spaces are not allowed")` | `ContainsValidationRule` | Validates that a string contains a specific substring | `ContainsValidationRule(substring: "@", error: "Must contain @")` | `EqualityValidationRule`| Validates that the input is equal to a given reference value | `EqualityValidationRule(compareTo: password, error: "Passwords do not match")` +| `ComparisonValidationRule` | Validates that input against a comparison constraint | `ComparisonValidationRule(greaterThan: 0, error: "Must be greater than 0")` ## Custom Validators diff --git a/Sources/ValidatorCore/Classes/Rules/ComparisonValidationRule.swift b/Sources/ValidatorCore/Classes/Rules/ComparisonValidationRule.swift new file mode 100644 index 0000000..2427640 --- /dev/null +++ b/Sources/ValidatorCore/Classes/Rules/ComparisonValidationRule.swift @@ -0,0 +1,91 @@ +// +// Validator +// Copyright © 2025 Space Code. All rights reserved. +// + +/// A validation rule that checks input against a comparison constraint. +/// +/// Supports comparisons such as: +/// - `>` (greater than) +/// - `>=` (greater than or equal to) +/// - `<` (less than) +/// - `<=` (less than or equal to) +/// +/// # Examples: +/// ```swift +/// let rule1 = ComparisonValidationRule(greaterThan: 0, error: "Must be greater than 0") +/// rule1.validate(input: 5) // true +/// rule1.validate(input: -1) // false +/// +/// let rule2 = ComparisonValidationRule(lessThanOrEqual: 100, error: "Must be <= 100") +/// rule2.validate(input: 100) // true +/// rule2.validate(input: 150) // false +/// ``` +public struct ComparisonValidationRule: IValidationRule { + // MARK: - Types + + public typealias Input = T + + /// Supported comparison operators. + public enum Condition { + case greaterThan(T) + case greaterThanOrEqual(T) + case lessThan(T) + case lessThanOrEqual(T) + } + + // MARK: - Properties + + /// The comparison condition to evaluate. + public let condition: Condition + + /// The validation error returned when validation fails. + public let error: IValidationError + + // MARK: - Initialization + + /// Creates a comparison-based validation rule. + /// + /// - Parameters: + /// - condition: The comparison condition. + /// - error: The validation error returned if validation fails. + public init(condition: Condition, error: IValidationError) { + self.condition = condition + self.error = error + } + + /// Convenience initializer for `greaterThan`. + public init(greaterThan value: T, error: IValidationError) { + self.init(condition: .greaterThan(value), error: error) + } + + /// Convenience initializer for `greaterThanOrEqual`. + public init(greaterThanOrEqual value: T, error: IValidationError) { + self.init(condition: .greaterThanOrEqual(value), error: error) + } + + /// Convenience initializer for `lessThan`. + public init(lessThan value: T, error: IValidationError) { + self.init(condition: .lessThan(value), error: error) + } + + /// Convenience initializer for `lessThanOrEqual`. + public init(lessThanOrEqual value: T, error: IValidationError) { + self.init(condition: .lessThanOrEqual(value), error: error) + } + + // MARK: - IValidationRule + + public func validate(input: T) -> Bool { + switch condition { + case let .greaterThan(value): + input > value + case let .greaterThanOrEqual(value): + input >= value + case let .lessThan(value): + input < value + case let .lessThanOrEqual(value): + input <= value + } + } +} diff --git a/Sources/ValidatorCore/Validator.docc/Overview.md b/Sources/ValidatorCore/Validator.docc/Overview.md index 64919c0..5f09abc 100644 --- a/Sources/ValidatorCore/Validator.docc/Overview.md +++ b/Sources/ValidatorCore/Validator.docc/Overview.md @@ -36,6 +36,7 @@ ValidatorCore contains all core validation rules, utilities, and mechanisms for - ``NoWhitespaceValidationRuleTests`` - ``ContainsValidationRule`` - ``EqualityValidationRule`` +- ``ComparisonValidationRule`` ### Articles diff --git a/Tests/ValidatorCoreTests/UnitTests/Rules/ComparisonValidationRuleTests.swift b/Tests/ValidatorCoreTests/UnitTests/Rules/ComparisonValidationRuleTests.swift new file mode 100644 index 0000000..fc1d328 --- /dev/null +++ b/Tests/ValidatorCoreTests/UnitTests/Rules/ComparisonValidationRuleTests.swift @@ -0,0 +1,50 @@ +// +// Validator +// Copyright © 2025 Space Code. All rights reserved. +// + +@testable import ValidatorCore +import XCTest + +// MARK: - ComparisonValidationRuleTests + +final class ComparisonValidationRuleTests: XCTestCase { + func test_greaterThanRule_passesForValidValue() { + let sut = ComparisonValidationRule(greaterThan: 10, error: String.error) + XCTAssertTrue(sut.validate(input: 20)) + } + + func test_greaterThanRule_failsForInvalidValue() { + let sut = ComparisonValidationRule(greaterThan: 10, error: String.error) + XCTAssertFalse(sut.validate(input: 10)) + } + + func test_greaterThanOrEqualRule_allowsEqualValue() { + let sut = ComparisonValidationRule(greaterThanOrEqual: 10, error: String.error) + XCTAssertTrue(sut.validate(input: 10)) + } + + func test_lessThanRule_passesForSmallerValues() { + let sut = ComparisonValidationRule(lessThan: 100, error: String.error) + XCTAssertTrue(sut.validate(input: 50)) + XCTAssertFalse(sut.validate(input: 100)) + } + + func test_lessThanOrEqualRule_allowsEqualValue() { + let sut = ComparisonValidationRule(lessThanOrEqual: 100, error: String.error) + XCTAssertTrue(sut.validate(input: 100)) + XCTAssertFalse(sut.validate(input: 101)) + } + + func test_ruleWorksWithStringComparable() { + let sut = ComparisonValidationRule(lessThan: "m", error: String.error) + XCTAssertTrue(sut.validate(input: "apple")) + XCTAssertFalse(sut.validate(input: "z")) + } +} + +// MARK: Constants + +private extension String { + static let error = "Invalid characters" +}