diff --git a/Sources/Validation/ValidationError.swift b/Sources/Validation/ValidationError.swift index 14a79f5..01fa3b5 100644 --- a/Sources/Validation/ValidationError.swift +++ b/Sources/Validation/ValidationError.swift @@ -7,6 +7,7 @@ import Debugging public protocol ValidationError: Debuggable { /// Key path to the invalid data. var path: [String] { get set } + var customMessage: String? { get set } } extension ValidationError { @@ -22,13 +23,17 @@ extension ValidationError { public struct BasicValidationError: ValidationError { /// See `Debuggable` public var reason: String { - let path: String - if self.path.count > 0 { - path = "'" + self.path.joined(separator: ".") + "'" + if let customMessage = customMessage { + return customMessage } else { - path = "data" + let path: String + if self.path.count > 0 { + path = "'" + self.path.joined(separator: ".") + "'" + } else { + path = "data" + } + return "\(path) \(message)" } - return "\(path) \(message)" } /// The validation failure @@ -36,6 +41,9 @@ public struct BasicValidationError: ValidationError { /// Key path the validation error happened at public var path: [String] + + /// See ValidationError.customMessage + public var customMessage: String? /// Create a new JWT error public init(_ message: String) { diff --git a/Sources/Validation/Validations.swift b/Sources/Validation/Validations.swift index e76857a..46d50c3 100644 --- a/Sources/Validation/Validations.swift +++ b/Sources/Validation/Validations.swift @@ -21,8 +21,9 @@ public struct Validations: CustomStringConvertible where M: Validatable { /// - keyPath: `KeyPath` to validatable property. /// - path: Readable path. Will be displayed when showing errors. /// - validation: `Validation` to run on this property. - public mutating func add(_ keyPath: KeyPath, at path: [String], _ validator: Validator) { - add(keyPath, at: path, "is " + validator.readable, { value in + /// - customMessage: Custom error message. + public mutating func add(_ keyPath: KeyPath, at path: [String], _ validator: Validator, customMessage: String? = nil) { + add(keyPath, at: path, "is " + validator.readable, customMessage: customMessage, { value in try validator.validate(value) }) } @@ -37,13 +38,15 @@ public struct Validations: CustomStringConvertible where M: Validatable { /// - keyPath: `KeyPath` to validatable property. /// - path: Readable path. Will be displayed when showing errors. /// - readable: Readable string describing this validation. + /// - customMessage: Custom error message. /// - custom: Closure accepting the `KeyPath`'s value. Throw a `ValidationError` here if the data is invalid. - public mutating func add(_ keyPath: KeyPath, at path: [String], _ readable: String, _ custom: @escaping (T) throws -> Void) { + public mutating func add(_ keyPath: KeyPath, at path: [String], _ readable: String, customMessage: String? = nil, _ custom: @escaping (T) throws -> Void) { add("\(path.joined(separator: ".")): \(readable)") { model in do { try custom(model[keyPath: keyPath]) } catch var error as ValidationError { error.path += path + error.customMessage = customMessage throw error } } @@ -91,8 +94,9 @@ extension Validations where M: Reflectable { /// - parameters: /// - keyPath: `KeyPath` to validatable property. /// - validation: `Validation` to run on this property. - public mutating func add(_ keyPath: KeyPath, _ validator: Validator) throws { - try add(keyPath, at: M.reflectProperty(forKey: keyPath)?.path ?? [], validator) + /// - customMessage: Custom error message. + public mutating func add(_ keyPath: KeyPath, _ validator: Validator, customMessage: String? = nil) throws { + try add(keyPath, at: M.reflectProperty(forKey: keyPath)?.path ?? [], validator, customMessage: customMessage) } @@ -105,24 +109,28 @@ extension Validations where M: Reflectable { /// - parameters: /// - keyPath: `KeyPath` to validatable property. /// - readable: Readable string describing this validation. + /// - customMessage: Custom error message. /// - custom: Closure accepting the `KeyPath`'s value. Throw a `ValidationError` here if the data is invalid. - public mutating func add(_ keyPath: KeyPath, _ readable: String, _ custom: @escaping (T) throws -> Void) throws { - try add(keyPath, at: M.reflectProperty(forKey: keyPath)?.path ?? [], readable, custom) + public mutating func add(_ keyPath: KeyPath, _ readable: String, customMessage: String? = nil, _ custom: @escaping (T) throws -> Void) throws { + try add(keyPath, at: M.reflectProperty(forKey: keyPath)?.path ?? [], readable, customMessage: customMessage, custom) } } // MARK: Private /// A collection of errors thrown by validatable models validations -fileprivate struct ValidateErrors: ValidationError { +public struct ValidateErrors: ValidationError { /// the errors thrown - var errors: [ValidationError] + public var errors: [ValidationError] /// See ValidationError.keyPath - var path: [String] + public var path: [String] + + /// See ValidationError.customMessage + public var customMessage: String? /// See ValidationError.reason - var reason: String { + public var reason: String { return errors.map { error in var mutableError = error mutableError.path = path + error.path diff --git a/Sources/Validation/Validators/AndValidator.swift b/Sources/Validation/Validators/AndValidator.swift index ff022b8..11f1728 100644 --- a/Sources/Validation/Validators/AndValidator.swift +++ b/Sources/Validation/Validators/AndValidator.swift @@ -48,15 +48,15 @@ fileprivate struct AndValidator: ValidatorType { } /// Error thrown if and validation fails -fileprivate struct AndValidatorError: ValidationError { +public struct AndValidatorError: ValidationError { /// error thrown by left validator - let left: ValidationError? + public let left: ValidationError? /// error thrown by right validator - let right: ValidationError? + public let right: ValidationError? /// See ValidationError.reason - var reason: String { + public var reason: String { if let left = left, let right = right { var mutableLeft = left, mutableRight = right mutableLeft.path = path + left.path @@ -76,7 +76,10 @@ fileprivate struct AndValidatorError: ValidationError { } /// See ValidationError.keyPath - var path: [String] + public var path: [String] + + /// See ValidationError.customMessage + public var customMessage: String? /// Creates a new or validator error init(_ left: ValidationError?, _ right: ValidationError?) { diff --git a/Sources/Validation/Validators/OrValidator.swift b/Sources/Validation/Validators/OrValidator.swift index c9e53a8..c66eaae 100644 --- a/Sources/Validation/Validators/OrValidator.swift +++ b/Sources/Validation/Validators/OrValidator.swift @@ -43,15 +43,15 @@ fileprivate struct OrValidator: ValidatorType { } /// Error thrown if or validation fails -fileprivate struct OrValidatorError: ValidationError { +public struct OrValidatorError: ValidationError { /// error thrown by left validator - let left: ValidationError + public let left: ValidationError /// error thrown by right validator - let right: ValidationError + public let right: ValidationError /// See ValidationError.reason - var reason: String { + public var reason: String { var left = self.left left.path = self.path + self.left.path var right = self.right @@ -60,7 +60,10 @@ fileprivate struct OrValidatorError: ValidationError { } /// See ValidationError.keyPath - var path: [String] + public var path: [String] + + /// See ValidationError.customMessage + public var customMessage: String? /// Creates a new or validator error init(_ left: ValidationError, _ right: ValidationError) {