mirror of
https://github.com/tappollo/makata.git
synced 2026-01-12 17:03:04 +08:00
98 lines
2.1 KiB
Swift
98 lines
2.1 KiB
Swift
//
|
|
// File.swift
|
|
//
|
|
//
|
|
// Created by Michael Ong on 2/3/23.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
public protocol Formable: AnyObject {
|
|
associatedtype FormData
|
|
|
|
var formHandler: FormHandler<FormData> { get }
|
|
|
|
func submitData(form: FormData) async throws
|
|
}
|
|
|
|
public extension Formable {
|
|
func submit() async throws {
|
|
try await formHandler.submit { [unowned self] shape in
|
|
try await submitData(form: shape)
|
|
}
|
|
}
|
|
}
|
|
|
|
@dynamicMemberLookup
|
|
public class FormHandler<Shape> {
|
|
public enum Errors: Error {
|
|
case invalid(FormValidation<Shape>.Result)
|
|
}
|
|
|
|
public struct State {
|
|
public let isValid: Bool
|
|
public let validationResult: FormValidation<Shape>.Result
|
|
}
|
|
|
|
public typealias UpdatesHandler = (Shape, State) async -> Void
|
|
|
|
public var current: Shape
|
|
|
|
var updateHandler: UpdatesHandler = { _, _ async in }
|
|
|
|
var validations: FormValidation<Shape>?
|
|
|
|
public init(initial: Shape) {
|
|
self.current = initial
|
|
}
|
|
|
|
func submit(_ callback: @escaping (Shape) async throws -> Void) async throws {
|
|
let errors = validations?.validate(current) ?? .noErrors
|
|
|
|
if !errors.fields.isEmpty {
|
|
throw Errors.invalid(errors)
|
|
}
|
|
|
|
try await callback(current)
|
|
}
|
|
|
|
func pushUpdates() {
|
|
Task {
|
|
let result = validations?.validate(current) ?? .noErrors
|
|
await updateHandler(current, .init(isValid: result.fields.isEmpty, validationResult: result))
|
|
}
|
|
}
|
|
}
|
|
|
|
extension FormHandler {
|
|
@discardableResult
|
|
public func callAsFunction(_ callback: @escaping UpdatesHandler) -> Self {
|
|
updateHandler = callback
|
|
|
|
pushUpdates()
|
|
|
|
return self
|
|
}
|
|
|
|
@discardableResult
|
|
public func setValidationHandler(_ handler: FormValidation<Shape>?) -> Self {
|
|
validations = handler
|
|
|
|
pushUpdates()
|
|
|
|
return self
|
|
}
|
|
}
|
|
|
|
extension FormHandler {
|
|
public subscript<Value>(dynamicMember member: WritableKeyPath<Shape, Value>) -> Value {
|
|
get {
|
|
current[keyPath: member]
|
|
}
|
|
set {
|
|
current[keyPath: member] = newValue
|
|
pushUpdates()
|
|
}
|
|
}
|
|
}
|