A small sketch for a composable validation library.
Created
April 25, 2017 04:56
-
-
Save lukesutton/357615cb8e4b53ae25165c50d2958da1 to your computer and use it in GitHub Desktop.
This file contains 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
enum ValidationFailure { | |
case valueExpected | |
case outOfRange(ValidationValue, ValidationValue) | |
case belowMinimum(ValidationValue) | |
case aboveMaximum(ValidationValue) | |
case onlyAllowed(ValidationValue) | |
case invalidFormat(ValidationValue) | |
case other(String, ValidationValue) | |
} | |
protocol ValidationValue { | |
var validationSummary: String { get } | |
} | |
extension Int: ValidationValue { | |
var validationSummary: String { | |
return description | |
} | |
} | |
extension Float: ValidationValue { | |
var validationSummary: String { | |
return description | |
} | |
} | |
extension Dictionary: ValidationValue { | |
var validationSummary: String { | |
return description | |
} | |
} | |
extension String: ValidationValue { | |
var validationSummary: String { | |
return description | |
} | |
} | |
extension Set: ValidationValue { | |
var validationSummary: String { | |
return description | |
} | |
} | |
extension Array: ValidationValue { | |
var validationSummary: String { | |
return description | |
} | |
} | |
struct ValidationResult { | |
let failures: [ValidationFailure] | |
var hasFailed: Bool { | |
return !failures.isEmpty | |
} | |
var hasPassed: Bool { | |
return failures.isEmpty | |
} | |
} | |
typealias Validator<T> = (T?) -> [ValidationFailure] | |
struct Validate<A> { | |
private let validator: Validator<A> | |
init(_ validator: @escaping Validator<A>) { | |
self.validator = validator | |
} | |
func run(_ value: A?) -> ValidationResult { | |
return ValidationResult(failures: validator(value)) | |
} | |
} | |
func required<A>(value: A?) -> [ValidationFailure] { | |
return value == nil ? [.valueExpected] : [] | |
} | |
func containOnly<A: Collection>(_ values: Set<A.Iterator.Element>) -> (A?) -> [ValidationFailure] { | |
return { value in | |
guard let value = value else { return [] } | |
return Set(value) == values ? [] : [.onlyAllowed(values)] | |
} | |
} | |
func oneOf<A: Equatable>(_ values: Set<A>) -> (A?) -> [ValidationFailure] { | |
return { value in | |
guard let value = value else { return [] } | |
return values.contains(value) ? [] : [.onlyAllowed(values)] | |
} | |
} | |
func minCount<A>(_ count: A.IndexDistance) -> (A?) -> [ValidationFailure] | |
where A: Collection, A.IndexDistance: ValidationValue { | |
return { value in | |
guard let value = value else { return [] } | |
return value.count >= count ? [] : [.belowMinimum(count)] | |
} | |
} | |
func maxCount<A: Collection>(_ count: A.IndexDistance) -> (A?) -> [ValidationFailure] | |
where A: Collection, A.IndexDistance: ValidationValue { | |
return { value in | |
guard let value = value else { return [] } | |
return value.count <= count ? [] : [.aboveMaximum(1)] | |
} | |
} | |
func maxValue<A>(_ count: A) -> (A?) -> [ValidationFailure] | |
where A: Comparable, A: ValidationValue { | |
return { value in | |
guard let value = value else { return [] } | |
return value <= count ? [] : [.belowMinimum(count)] | |
} | |
} | |
func minValue<A>(_ count: A) -> (A?) -> [ValidationFailure] | |
where A: Comparable, A: ValidationValue { | |
return { value in | |
guard let value = value else { return [] } | |
return value >= count ? [] : [.aboveMaximum(count)] | |
} | |
} | |
func &&<A>(lhs: @escaping Validator<A>, rhs: @escaping Validator<A>) -> Validator<A> { | |
return { value in | |
return lhs(value) + rhs(value) | |
} | |
} | |
func ||<A>(lhs: @escaping Validator<A>, rhs: @escaping Validator<A>) -> Validator<A> { | |
return { value in | |
let x = lhs(value) | |
let y = rhs(value) | |
if x.isEmpty || y.isEmpty { | |
return [] | |
} | |
else { | |
return x + y | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment