Skip to content

Commit

Permalink
Template Actions
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex Usbergo committed May 19, 2020
1 parent 71aa7d4 commit bc6cec2
Showing 1 changed file with 40 additions and 28 deletions.
68 changes: 40 additions & 28 deletions Sources/Store/TemplateActions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,84 +3,96 @@ import os.log

// MARK: - Template Actions

public enum KeyPath<M, V> {
/// A non-optional writeable keyPath.
case value(keyPath: WritableKeyPath<M, V>)
/// A optional writeable keyPath.
case optional(keyPath: WritableKeyPath<M, V?>)
}

/// General-purpose actions that can be applied to any store.
public struct TemplateAction {

public struct AssignKeyPath<M, V>: ActionProtocol {
public let keyPath: WritableKeyPath<M, V>
public let value: V

public func reduce(context: TransactionContext<Store<M>, Self>) {
defer { context.fulfill() }
context.reduceModel { model in model[keyPath: keyPath] = value }
}
}

public struct AssignOptionalKeyPath<M, V>: ActionProtocol {
public let keyPath: WritableKeyPath<M, V?>
public struct AssignKeyPath<M, V>: ActionProtocol {
public let keyPath: KeyPath<M, V>
public let value: V?

public func reduce(context: TransactionContext<Store<M>, Self>) {
defer { context.fulfill() }
context.reduceModel { model in model[keyPath: keyPath] = value }
context.reduceModel { model in
_assignKeyPath(object: &model, keyPath: keyPath, value: value)
}
}
}

public struct Filter<M, V: Collection, T>: ActionProtocol where V.Element == T {
public let keyPath: WritableKeyPath<M, V>
public let keyPath: KeyPath<M, V>
public let isIncluded: (T) -> Bool

public func reduce(context: TransactionContext<Store<M>, Self>) {
defer { context.fulfill() }
context.reduceModel { model in
_mutateArray(object: &model, keyPath: keyPath) { array in
array = array.filter(isIncluded)
}
_mutateArray(object: &model, keyPath: keyPath) { $0 = $0.filter(isIncluded) }
}
}
}

public struct RemoveAtIndex<M, V: Collection, T>: ActionProtocol where V.Element == T {
public let keyPath: WritableKeyPath<M, V>
public let keyPath: KeyPath<M, V>
public let index: Int

public func reduce(context: TransactionContext<Store<M>, Self>) {
defer { context.fulfill() }
context.reduceModel { model in
_mutateArray(object: &model, keyPath: keyPath) { array in
array.remove(at: index)
}
_mutateArray(object: &model, keyPath: keyPath) { $0.remove(at: index) }
}
}
}

public struct Push<M, V: Collection, T>: ActionProtocol where V.Element == T {
public let keyPath: WritableKeyPath<M, V>
public let keyPath: KeyPath<M, V>
public let object: T

public func reduce(context: TransactionContext<Store<M>, Self>) {
defer { context.fulfill() }
context.reduceModel { model in
_mutateArray(object: &model, keyPath: keyPath) { array in
array.append(object)
}
_mutateArray(object: &model, keyPath: keyPath) { $0.append(object) }
}
}
}
}

private func _mutateArray<M, V: Collection, T>(
object: inout M,
keyPath: WritableKeyPath<M, V>,
keyPath: KeyPath<M, V>,
mutate: (inout [T]) -> Void
) where V.Element == T {
guard var array = object[keyPath: keyPath] as? [T] else {
var value: V
switch keyPath {
case .value(let keyPath): value = object[keyPath: keyPath]
case .optional(let keyPath):
guard let unwrapped = object[keyPath: keyPath] else { return }
value = unwrapped
}
guard var array = value as? [T] else {
os_log(.error, log: OSLog.primary, " Arrays are the only collection type supported.")
return
}
mutate(&array)
// Trivial cast.
guard let collection = array as? V else { return }
object[keyPath: keyPath] = collection
switch keyPath {
case .value(let keyPath): object[keyPath: keyPath] = collection
case .optional(let keyPath): object[keyPath: keyPath] = collection
}
}

private func _assignKeyPath<M, V>(object: inout M, keyPath: KeyPath<M, V>, value: V?) {
switch keyPath {
case .value(let keyPath):
guard let value = value else { return }
object[keyPath: keyPath] = value
case .optional(let keyPath):
object[keyPath: keyPath] = value
}
}

0 comments on commit bc6cec2

Please sign in to comment.