131 lines
3.7 KiB
Swift
131 lines
3.7 KiB
Swift
//
|
|
// FormFieldable.swift
|
|
// VDS
|
|
//
|
|
// Created by Matt Bruce on 7/22/22.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
/// Protocol used for a FormField object.
|
|
public protocol FormFieldable {
|
|
associatedtype ValueType = AnyHashable
|
|
|
|
/// Unique Id for the Form Field object within a Form.
|
|
var inputId: String? { get set }
|
|
|
|
/// Value for the Form Field.
|
|
var value: ValueType? { get }
|
|
}
|
|
|
|
/// Protocol for FormFieldable that require internal validation.
|
|
public protocol FormFieldInternalValidatable: FormFieldable, Errorable {
|
|
/// Rules that drive the validator
|
|
var rules: [AnyRule<ValueType>] { get set }
|
|
|
|
/// Is there an internalError
|
|
var hasInternalError: Bool { get }
|
|
/// Internal Error Message that will show.
|
|
var internalErrorText: String? { get }
|
|
|
|
var validator: (any FormFieldValidatorable)? { get set }
|
|
|
|
func validate()
|
|
}
|
|
|
|
extension FormFieldInternalValidatable {
|
|
/// Whether or not to show the internal error
|
|
public var hasInternalError: Bool {
|
|
guard let validator, !showError else { return false }
|
|
return !validator.isValid
|
|
}
|
|
|
|
public var internalErrorText: String? {
|
|
guard let validator, !validator.isValid else { return nil }
|
|
if let errorText, !errorText.isEmpty {
|
|
return errorText
|
|
} else {
|
|
return validator.errorMessage
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Struct that will execute the validation.
|
|
public protocol FormFieldValidatorable {
|
|
associatedtype FieldType: FormFieldable
|
|
/// FormFieldable to be validated.
|
|
var field: FieldType { get set }
|
|
/// Rules that will be applied against the FormFieldable
|
|
var rules: [AnyRule<FieldType.ValueType>] { get set }
|
|
/// Error Message that will show.
|
|
var errorMessage: String? { get }
|
|
/// Is the FormField valid.
|
|
var isValid: Bool { get }
|
|
/// Run the rules against the FormFieldable.
|
|
func validate()
|
|
}
|
|
|
|
/// Rule that will be executed against a specific ValueType.
|
|
public protocol Rule<ValueType> {
|
|
associatedtype ValueType
|
|
/// Determines if this rule valid for the value passed.
|
|
func isValid(value: ValueType?) -> Bool
|
|
/// Error Message to be show if the value is invalid.
|
|
var errorMessage: String { get }
|
|
/// type of rule
|
|
var ruleType: String { get }
|
|
}
|
|
|
|
extension Rule {
|
|
public var ruleType: String { "\(Self.self)" }
|
|
}
|
|
|
|
/// Type Erased Rule for a specific ValueType.
|
|
public struct AnyRule<ValueType>: Rule {
|
|
private let _isValid: (ValueType?) -> Bool
|
|
public var ruleType: String
|
|
public let errorMessage: String
|
|
|
|
public init<R: Rule>(_ rule: R) where R.ValueType == ValueType {
|
|
self._isValid = rule.isValid
|
|
self.ruleType = rule.ruleType
|
|
self.errorMessage = rule.errorMessage
|
|
}
|
|
|
|
public func isValid(value: ValueType?) -> Bool {
|
|
return _isValid(value)
|
|
}
|
|
}
|
|
|
|
/// Generic Validator for a specific FormFieldable.
|
|
public class FormFieldValidator<Field:FormFieldable>: FormFieldValidatorable{
|
|
public var field: Field
|
|
public var rules: [AnyRule<Field.ValueType>]
|
|
public var errorMessages = [String]()
|
|
public var isValid: Bool = true
|
|
|
|
public init(field: Field, rules: [AnyRule<Field.ValueType>]) {
|
|
self.field = field
|
|
self.rules = rules
|
|
}
|
|
|
|
public var errorMessage: String? {
|
|
guard errorMessages.count > 0 else { return nil }
|
|
return errorMessages.joined(separator: "\r")
|
|
}
|
|
|
|
public func validate() {
|
|
errorMessages.removeAll()
|
|
|
|
for rule in rules {
|
|
if !rule.isValid(value: field.value) {
|
|
errorMessages.append(rule.errorMessage)
|
|
isValid = false
|
|
return
|
|
}
|
|
}
|
|
isValid = true
|
|
}
|
|
|
|
}
|