Skip to content

Instantly share code, notes, and snippets.

@stinger
Last active December 28, 2020 11:06
Show Gist options
  • Save stinger/29c5cad1d759fb5f9a9967ebb285a612 to your computer and use it in GitHub Desktop.
Save stinger/29c5cad1d759fb5f9a9967ebb285a612 to your computer and use it in GitHub Desktop.
Functional String validation
func validate<T: Codable>(_ validator: @escaping (T) -> Bool) -> (T) -> Bool {
return { input in
return validator(input)
}
}
enum StringValidator: CustomStringConvertible {
case isNotEmpty
case lengthIn(ClosedRange<Int>)
var validator: (String) -> Bool {
return validate { input in
switch self {
case .isNotEmpty:
return !input.isEmpty
case .lengthIn(let lengthRange):
return lengthRange.contains(input.count)
}
}
}
var description: String {
switch self {
case .isNotEmpty:
return "Input should not be empty"
case .lengthIn(let lengthRange):
return "Input should be between \(lengthRange.lowerBound) and \(lengthRange.upperBound) characters long"
}
}
}
extension String {
func failedValidators(in validators: StringValidator...) -> [StringValidator] {
return validators.filter { !$0.validator(self) }
}
}
/// Usage
let input = "Test"
print("Input: \"\(input)\"")
let failed = input.failedValidators(in: .isNotEmpty, .lengthIn(5...40))
/// Should be an error regarding the length
print("is valid:", failed.isEmpty)
if !failed.isEmpty {
print(failed.map { $0.description }.joined(separator: "\n"))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment