Created
October 20, 2015 14:06
-
-
Save nRewik/c2cfcc041928db562df8 to your computer and use it in GitHub Desktop.
A rule-based string validator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//: Playground - noun: a place where people can play | |
import UIKit | |
import XCPlayground | |
public typealias Validator = String -> Bool | |
public typealias ValidatorTransformer = Validator -> Validator | |
public typealias ValidatorCombiner = ( Validator , Validator ) -> Validator | |
public enum Rule{ | |
case regex( pattern: String ) | |
case email | |
case phoneNumber | |
case containsOnly( [Character] ) | |
case alphabet | |
case number | |
case length( ( (Int,Int) -> Bool) , Int ) | |
case custom( Validator ) | |
// operator | |
indirect case transform( Rule , ValidatorTransformer ) | |
indirect case combine( Rule, Rule, ValidatorCombiner ) | |
func validate(text: String) -> Bool{ | |
switch self{ | |
case let .regex(pattern): | |
let _nsregex = try? NSRegularExpression(pattern: pattern, options: []) | |
guard let nsregex = _nsregex else { return false } | |
let numberOfMathches = nsregex.numberOfMatchesInString(text, options: [], range: NSRange(location: 0,length: text.utf16.count)) | |
return numberOfMathches > 0 | |
case .email: | |
let emailPattern = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}" | |
return Rule.regex(pattern: emailPattern).validate(text) | |
// phoneNumber | |
case .phoneNumber: | |
let phonePattern = "^\\d{10}$" | |
return Rule.regex(pattern: phonePattern).validate(text) | |
// containsOnly | |
case let .containsOnly(characters): | |
let characterSet = Set<Character>(characters) | |
for character in text.characters{ | |
if !characterSet.contains(character){ | |
return false | |
} | |
} | |
return true | |
// alphabet | |
case .alphabet: | |
let alphabets = "abcdefghijklmnopqrstuvwxyz".characters.flatMap{ $0 } | |
return Rule.containsOnly(alphabets).validate(text.lowercaseString) | |
// number | |
case .number: | |
let numbers = "1234567890".characters.flatMap{ $0 } | |
return Rule.containsOnly(numbers).validate(text) | |
// length | |
case let .length(comparator, textCount): | |
return comparator(text.characters.count,textCount) | |
// custom | |
case let .custom(validator): | |
return validator(text) | |
// operator | |
case let .transform( rule, transformer): | |
return transformer(rule.validate)(text) | |
case let .combine( leftRule, rightRule, combiner): | |
return combiner( leftRule.validate, rightRule.validate )(text) | |
} | |
} | |
} | |
////////////////////// | |
// String Extension // | |
////////////////////// | |
public extension String{ | |
// varadic template | |
public func isValid( rules: Rule...) -> Bool{ | |
return ( rules.filter{ $0.validate(self) == false } ).count == 0 | |
} | |
// explicit array | |
public func isValid( rules: [Rule]) -> Bool{ | |
return ( rules.filter{ $0.validate(self) == false } ).count == 0 | |
} | |
// collection type | |
public func isValid<C: CollectionType where C.Generator.Element == Rule> ( rules: C) -> Bool{ | |
return ( rules.filter{ $0.validate(self) == false } ).count == 0 | |
} | |
} | |
////////////////////// | |
// Custom Operator // | |
////////////////////// | |
// negate Rule shortcut | |
public prefix func ! (rule: Rule) -> Rule{ | |
return Rule.transform(rule, !) | |
} | |
// negate transformer | |
public prefix func ! ( validator: Validator) -> Validator{ | |
return { !validator($0) } | |
} | |
// and Rule shortcut | |
public func && ( left: Rule , right: Rule) -> Rule{ | |
return Rule.combine(left, right, &&) | |
} | |
// and combiner | |
public func && ( leftValidator: Validator , rightValidator: Validator) -> Validator{ | |
return { leftValidator($0) && rightValidator($0) } | |
} | |
// or Rule shortcut | |
public func || ( left: Rule , right: Rule) -> Rule{ | |
return Rule.combine(left, right, ||) | |
} | |
// or combiner | |
public func || ( leftValidator: Validator , rightValidator: Validator) -> Validator{ | |
return { leftValidator($0) || rightValidator($0) } | |
} | |
// matching operator | |
infix operator ~= {} | |
public func ~= ( text: String, rule: Rule) -> Bool{ | |
return text.isValid(rule) | |
} | |
public func ~= ( text: String, rules: [Rule]) -> Bool{ | |
return text.isValid(rules) | |
} | |
// matcher for switch..case | |
public func ~= ( rule: Rule, text: String) -> Bool{ | |
return text.isValid(rule) | |
} | |
public func ~= ( rules: [Rule], text: String) -> Bool{ | |
return text.isValid(rules) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment