Skip to content

Instantly share code, notes, and snippets.

@nRewik
Created October 20, 2015 14:06
Show Gist options
  • Save nRewik/c2cfcc041928db562df8 to your computer and use it in GitHub Desktop.
Save nRewik/c2cfcc041928db562df8 to your computer and use it in GitHub Desktop.
A rule-based string validator
//: 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
// email
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