From 11ddfb04c300f3121d93f669f52210bc01d8b975 Mon Sep 17 00:00:00 2001 From: Michael Housh Date: Thu, 30 May 2024 17:48:34 -0400 Subject: [PATCH] feat: Adding more helpers, wip --- .../ComposableSubscriber/Effect+receive.swift | 14 ---- .../ComposableSubscriber/OnFailAction.swift | 30 +++++++++ .../ComposableSubscriber/ReceiveAction.swift | 15 +++++ .../ComposableSubscriber/ReceiveReducer.swift | 66 +++++++++++-------- .../TCAExtrasTests.swift | 27 ++++---- 5 files changed, 99 insertions(+), 53 deletions(-) create mode 100644 Sources/ComposableSubscriber/OnFailAction.swift create mode 100644 Sources/ComposableSubscriber/ReceiveAction.swift diff --git a/Sources/ComposableSubscriber/Effect+receive.swift b/Sources/ComposableSubscriber/Effect+receive.swift index 08f6761..8ff8022 100644 --- a/Sources/ComposableSubscriber/Effect+receive.swift +++ b/Sources/ComposableSubscriber/Effect+receive.swift @@ -1,20 +1,6 @@ import ComposableArchitecture import OSLog -public protocol ReceiveAction { - associatedtype ReceiveAction - static func receive(_ result: TaskResult) -> Self - - var result: TaskResult? { get } -} - -extension ReceiveAction { - - public var result: TaskResult? { - AnyCasePath(unsafe: Self.receive).extract(from: self) - } -} - extension Effect where Action: ReceiveAction { public static func receive( diff --git a/Sources/ComposableSubscriber/OnFailAction.swift b/Sources/ComposableSubscriber/OnFailAction.swift new file mode 100644 index 0000000..68963d0 --- /dev/null +++ b/Sources/ComposableSubscriber/OnFailAction.swift @@ -0,0 +1,30 @@ +import ComposableArchitecture +import Foundation +import OSLog + +public enum OnFailAction { + case fail(prefix: String? = nil, log: ((String) -> Void)? = nil) + case handle((inout State, Error) -> Void) + + @available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) + @inlinable + static func fail(prefix: String? = nil, logger: Logger) -> Self { + .fail(prefix: prefix, log: { logger.error("\($0)") }) + } + + @usableFromInline + func callAsFunction(state: inout State, error: Error) -> Effect { + switch self { + case let .fail(prefix, log): + if let prefix { + return .fail(prefix: prefix, error: error, log: log) + } else { + return .fail(error: error, log: log) + } + case let .handle(handler): + handler(&state, error) + return .none + } + } + +} diff --git a/Sources/ComposableSubscriber/ReceiveAction.swift b/Sources/ComposableSubscriber/ReceiveAction.swift new file mode 100644 index 0000000..74e0f80 --- /dev/null +++ b/Sources/ComposableSubscriber/ReceiveAction.swift @@ -0,0 +1,15 @@ +import ComposableArchitecture + +public protocol ReceiveAction { + associatedtype ReceiveAction + static func receive(_ result: TaskResult) -> Self + + var result: TaskResult? { get } +} + +extension ReceiveAction { + + public var result: TaskResult? { + AnyCasePath(unsafe: Self.receive).extract(from: self) + } +} diff --git a/Sources/ComposableSubscriber/ReceiveReducer.swift b/Sources/ComposableSubscriber/ReceiveReducer.swift index 8c7e977..2fe3ded 100644 --- a/Sources/ComposableSubscriber/ReceiveReducer.swift +++ b/Sources/ComposableSubscriber/ReceiveReducer.swift @@ -105,35 +105,43 @@ extension Reducer { } } } -} -public enum OnFailAction { - case fail(prefix: String? = nil, log: ((String) -> Void)? = nil) - case handle((inout State, Error) -> Void) - - @available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) @inlinable - static func fail(prefix: String? = nil, logger: Logger) -> Self { - .fail(prefix: prefix, log: { logger.error("\($0)") }) - } - - @usableFromInline - func callAsFunction(state: inout State, error: Error) -> Effect { - switch self { - case let .fail(prefix, log): - if let prefix { - return .fail(prefix: prefix, error: error, log: log) - } else { - return .fail(error: error, log: log) - } - case let .handle(handler): - handler(&state, error) - return .none - } + public func receive( + on triggerAction: CaseKeyPath, + with receiveAction: CaseKeyPath>, + result resultHandler: @escaping @Sendable () async throws -> Value + ) -> _ReceiveOnTriggerReducer { + .init( + parent: self, + triggerAction: { AnyCasePath(triggerAction).extract(from: $0) }, + toReceiveAction: { AnyCasePath(receiveAction).embed($0) }, + resultHandler: resultHandler + ) } } +extension Reducer where Action: ReceiveAction { + @inlinable + public func receive( + on triggerAction: CaseKeyPath, + with embedCasePath: CaseKeyPath, + result resultHandler: @escaping @Sendable () async throws -> Value + ) -> _ReceiveOnTriggerReducer { + .init( + parent: self, + triggerAction: { AnyCasePath(triggerAction).extract(from: $0) }, + toReceiveAction: { AnyCasePath(unsafe: Action.receive).embed($0) }, + resultHandler: { + try await AnyCasePath(embedCasePath).embed( + resultHandler() + ) + } + ) + } +} + extension WritableKeyPath { @usableFromInline @@ -181,7 +189,11 @@ public struct _ReceiveReducer: Reducer { } } -public struct _OnRecieveReducer: Reducer { +public struct _ReceiveOnTriggerReducer< + Parent: Reducer, + TriggerAction, + Value +>: Reducer { @usableFromInline let parent: Parent @@ -198,9 +210,9 @@ public struct _OnRecieveReducer: Reducer @usableFromInline init( parent: Parent, - triggerAction: @escaping (Parent.Action) -> TriggerAction?, - toReceiveAction: @escaping (TaskResult) -> Parent.Action, - resultHandler: @escaping () -> Value + triggerAction: @escaping @Sendable (Parent.Action) -> TriggerAction?, + toReceiveAction: @escaping @Sendable (TaskResult) -> Parent.Action, + resultHandler: @escaping @Sendable () async throws -> Value ) { self.parent = parent self.triggerAction = triggerAction diff --git a/Tests/swift-composable-subscriberTests/TCAExtrasTests.swift b/Tests/swift-composable-subscriberTests/TCAExtrasTests.swift index 3c80671..53ce0b5 100644 --- a/Tests/swift-composable-subscriberTests/TCAExtrasTests.swift +++ b/Tests/swift-composable-subscriberTests/TCAExtrasTests.swift @@ -127,19 +127,22 @@ struct ReducerWithReceiveAction { return .none } } - - Reduce { state, action in - switch action { - - case .receive: - return .none - - case .task: - return .receive(\.currentNumber) { - try await numberClient.currentNumber() - } - } + .receive(on: \.task, with: \.currentNumber) { + try await numberClient.currentNumber() } + +// Reduce { state, action in +// switch action { +// +// case .receive: +// return .none +// +// case .task: +// return .receive(\.currentNumber) { +// try await numberClient.currentNumber() +// } +// } +// } } }