// // 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 { /// Is there an internalError var hasInternalError: Bool { get } /// Internal Error Message that will show. var internalErrorText: String? { get } } /// 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] { 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 { 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 Erased Rule for a specific ValueType. public struct AnyRule: Rule { private let _isValid: (ValueType?) -> Bool public let errorMessage: String public init(_ rule: R) where R.ValueType == ValueType { self._isValid = rule.isValid self.errorMessage = rule.errorMessage } public func isValid(value: ValueType?) -> Bool { return _isValid(value) } } /// Generic Validator for a specific FormFieldable. public class FormFieldValidator: FormFieldValidatorable{ public var field: Field public var rules: [AnyRule] public var errorMessages = [String]() public var isValid: Bool = true public init(field: Field, rules: [AnyRule]) { 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 } }