From eefbb0ca20607e43e23d3f9a82df32e8d303df46 Mon Sep 17 00:00:00 2001 From: Jamie Quadri Date: Thu, 3 Apr 2025 15:53:02 -0500 Subject: [PATCH] [NFC]: reformatting and some generic syntax sugar --- Workflow/Sources/AnyWorkflow.swift | 26 ++++++++-- Workflow/Sources/AnyWorkflowConvertible.swift | 35 +++++++++++-- Workflow/Sources/RenderContext.swift | 49 +++++++++++++++---- Workflow/Sources/SubtreeManager.swift | 15 +++--- Workflow/Sources/WorkflowAction.swift | 2 +- Workflow/Sources/WorkflowObserver.swift | 8 +-- Workflow/Tests/WorkflowObserverTests.swift | 4 +- .../Internal/RenderTester+TestContext.swift | 6 +-- .../Sources/WorkflowActionTester.swift | 2 +- .../Sources/WorkflowRenderTester.swift | 4 +- 10 files changed, 113 insertions(+), 38 deletions(-) diff --git a/Workflow/Sources/AnyWorkflow.swift b/Workflow/Sources/AnyWorkflow.swift index c6e347bc..0a76e750 100644 --- a/Workflow/Sources/AnyWorkflow.swift +++ b/Workflow/Sources/AnyWorkflow.swift @@ -66,7 +66,9 @@ extension AnyWorkflow { /// - Parameter transform: An escaping closure that maps the original output type into the new output type. /// /// - Returns: A type erased workflow with the new output type (the rendering type remains unchanged). - public func mapOutput(_ transform: @escaping (Output) -> NewOutput) -> AnyWorkflow { + public func mapOutput( + _ transform: @escaping (Output) -> NewOutput + ) -> AnyWorkflow { let storage = storage.mapOutput(transform: transform) return AnyWorkflow(storage: storage) } @@ -76,7 +78,9 @@ extension AnyWorkflow { /// - Parameter transform: An escaping closure that maps the original rendering type into the new rendering type. /// /// - Returns: A type erased workflow with the new rendering type (the output type remains unchanged). - public func mapRendering(_ transform: @escaping (Rendering) -> NewRendering) -> AnyWorkflow { + public func mapRendering( + _ transform: @escaping (Rendering) -> NewRendering + ) -> AnyWorkflow { let storage = storage.mapRendering(transform: transform) return AnyWorkflow(storage: storage) } @@ -91,7 +95,11 @@ extension AnyWorkflow { /// That type information *is* present in our storage object, however, so we /// pass the context down to that storage object which will ultimately call /// through to `context.render(workflow:key:reducer:)`. - func render(context: RenderContext, key: String, outputMap: @escaping (Output) -> Action) -> Rendering where Action: WorkflowAction, Action.WorkflowType == Parent { + func render( + context: RenderContext, + key: String, + outputMap: @escaping (Output) -> Action + ) -> Rendering where Action.WorkflowType == Parent { storage.render(context: context, key: key, outputMap: outputMap) } } @@ -103,7 +111,11 @@ extension AnyWorkflow { fileprivate class AnyStorage { var base: Any { fatalError() } - func render(context: RenderContext, key: String, outputMap: @escaping (Output) -> Action) -> Rendering where Action: WorkflowAction, Action.WorkflowType == Parent { + func render( + context: RenderContext, + key: String, + outputMap: @escaping (Output) -> Action + ) -> Rendering where Action.WorkflowType == Parent { fatalError() } @@ -140,7 +152,11 @@ extension AnyWorkflow { T.self } - override func render(context: RenderContext, key: String, outputMap: @escaping (Output) -> Action) -> Rendering where Action: WorkflowAction, Action.WorkflowType == Parent { + override func render( + context: RenderContext, + key: String, + outputMap: @escaping (Output) -> Action + ) -> Rendering where Action.WorkflowType == Parent { let outputMap: (T.Output) -> Action = { [outputTransform] output in outputMap(outputTransform(output)) } diff --git a/Workflow/Sources/AnyWorkflowConvertible.swift b/Workflow/Sources/AnyWorkflowConvertible.swift index 2de3f986..9344c6dd 100644 --- a/Workflow/Sources/AnyWorkflowConvertible.swift +++ b/Workflow/Sources/AnyWorkflowConvertible.swift @@ -37,15 +37,31 @@ extension AnyWorkflowConvertible { /// - Parameter key: A string that uniquely identifies this workflow. /// /// - Returns: The `Rendering` generated by the workflow. - public func rendered(in context: RenderContext, key: String = "") -> Rendering where Output: WorkflowAction, Output.WorkflowType == Parent { + public func rendered( + in context: RenderContext, + key: String = "" + ) -> Rendering + where Output: WorkflowAction, Output.WorkflowType == Parent + { asAnyWorkflow().render(context: context, key: key, outputMap: { $0 }) } - public func rendered(in context: RenderContext, key: String = "", outputMap: @escaping (Output) -> Action) -> Rendering where Action: WorkflowAction, Action.WorkflowType == Parent { + public func rendered( + in context: RenderContext, + key: String = "", + outputMap: @escaping (Output) -> Action + ) -> Rendering + where Action.WorkflowType == Parent + { asAnyWorkflow().render(context: context, key: key, outputMap: { outputMap($0) }) } - public func rendered(in context: RenderContext, key: String = "") -> Rendering where Output == AnyWorkflowAction { + public func rendered( + in context: RenderContext, + key: String = "" + ) -> Rendering + where Output == AnyWorkflowAction + { asAnyWorkflow().render(context: context, key: key, outputMap: { $0 }) } } @@ -73,13 +89,22 @@ extension AnyWorkflowConvertible where Output == Never { } extension AnyWorkflowConvertible where Rendering == Void { - public func running(in context: RenderContext, key: String = "", outputMap: @escaping (Output) -> Action) where Action: WorkflowAction, Action.WorkflowType == Parent { + public func running( + in context: RenderContext, + key: String = "", + outputMap: @escaping (Output) -> Action + ) + where Action.WorkflowType == Parent + { rendered(in: context, key: key, outputMap: outputMap) } } extension AnyWorkflowConvertible where Rendering == Void, Output: WorkflowAction { - public func running(in context: RenderContext, key: String = "") where Output.WorkflowType == Parent { + public func running( + in context: RenderContext, + key: String = "" + ) where Output.WorkflowType == Parent { rendered(in: context, key: key) } } diff --git a/Workflow/Sources/RenderContext.swift b/Workflow/Sources/RenderContext.swift index eb099a95..c9951b63 100644 --- a/Workflow/Sources/RenderContext.swift +++ b/Workflow/Sources/RenderContext.swift @@ -63,7 +63,11 @@ public class RenderContext: RenderContextType { /// - Parameter key: A string that uniquely identifies this child. /// /// - Returns: The `Rendering` result of the child's `render` method. - func render(workflow: Child, key: String, outputMap: @escaping (Child.Output) -> Action) -> Child.Rendering where Child: Workflow, Action: WorkflowAction, WorkflowType == Action.WorkflowType { + func render( + workflow: Child, + key: String, + outputMap: @escaping (Child.Output) -> Action + ) -> Child.Rendering where WorkflowType == Action.WorkflowType { fatalError() } @@ -77,7 +81,9 @@ public class RenderContext: RenderContextType { /// /// - Parameter actionType: The type of Action this Sink may process /// - Returns: A Sink capable of relaying `Action` instances to the Workflow runtime - public func makeSink(of actionType: Action.Type) -> Sink where Action: WorkflowAction, Action.WorkflowType == WorkflowType { + public func makeSink( + of actionType: Action.Type + ) -> Sink where Action.WorkflowType == WorkflowType { fatalError() } @@ -118,17 +124,30 @@ public class RenderContext: RenderContextType { super.init() } - override func render(workflow: Child, key: String, outputMap: @escaping (Child.Output) -> Action) -> Child.Rendering where WorkflowType == Action.WorkflowType, Child: Workflow, Action: WorkflowAction { + override func render( + workflow: Child, + key: String, + outputMap: @escaping (Child.Output) -> Action + ) -> Child.Rendering + where WorkflowType == Action.WorkflowType + { assertStillValid() return implementation.render(workflow: workflow, key: key, outputMap: outputMap) } - override func makeSink(of actionType: Action.Type) -> Sink where WorkflowType == Action.WorkflowType, Action: WorkflowAction { + override func makeSink( + of actionType: Action.Type + ) -> Sink + where WorkflowType == Action.WorkflowType + { assertStillValid() return implementation.makeSink(of: actionType) } - override func runSideEffect(key: AnyHashable, action: (_ lifetime: Lifetime) -> Void) { + override func runSideEffect( + key: AnyHashable, + action: (_ lifetime: Lifetime) -> Void + ) { assertStillValid() implementation.runSideEffect(key: key, action: action) } @@ -142,15 +161,27 @@ public class RenderContext: RenderContextType { protocol RenderContextType: AnyObject { associatedtype WorkflowType: Workflow - func render(workflow: Child, key: String, outputMap: @escaping (Child.Output) -> Action) -> Child.Rendering where Child: Workflow, Action: WorkflowAction, Action.WorkflowType == WorkflowType + func render( + workflow: Child, + key: String, + outputMap: @escaping (Child.Output) -> Action + ) -> Child.Rendering where Action.WorkflowType == WorkflowType - func makeSink(of actionType: Action.Type) -> Sink where Action: WorkflowAction, Action.WorkflowType == WorkflowType + func makeSink( + of actionType: Action.Type + ) -> Sink where Action.WorkflowType == WorkflowType - func runSideEffect(key: AnyHashable, action: (_ lifetime: Lifetime) -> Void) + func runSideEffect( + key: AnyHashable, + action: (_ lifetime: Lifetime) -> Void + ) } extension RenderContext { - public func makeSink(of eventType: Event.Type, onEvent: @escaping (Event, inout WorkflowType.State) -> WorkflowType.Output?) -> Sink { + public func makeSink( + of eventType: Event.Type, + onEvent: @escaping (Event, inout WorkflowType.State) -> WorkflowType.Output? + ) -> Sink { makeSink(of: AnyWorkflowAction.self) .contraMap { event in AnyWorkflowAction { state in diff --git a/Workflow/Sources/SubtreeManager.swift b/Workflow/Sources/SubtreeManager.swift index a41e6703..730d7c10 100644 --- a/Workflow/Sources/SubtreeManager.swift +++ b/Workflow/Sources/SubtreeManager.swift @@ -199,14 +199,12 @@ extension WorkflowNode.SubtreeManager { self.session = session } - func render( + func render( workflow: Child, key: String, outputMap: @escaping (Child.Output) -> Action ) -> Child.Rendering - where Child: Workflow, - Action: WorkflowAction, - WorkflowType == Action.WorkflowType + where WorkflowType == Action.WorkflowType { /// A unique key used to identify this child workflow let childKey = ChildKey(childType: Child.self, key: key) @@ -256,7 +254,9 @@ extension WorkflowNode.SubtreeManager { return child.render() } - func makeSink(of actionType: Action.Type) -> Sink where Action: WorkflowAction, WorkflowType == Action.WorkflowType { + func makeSink( + of actionType: Action.Type + ) -> Sink where WorkflowType == Action.WorkflowType { let reusableSink = sinkStore.findOrCreate(actionType: Action.self) let sink = Sink { [weak reusableSink] action in @@ -269,7 +269,10 @@ extension WorkflowNode.SubtreeManager { return sink } - func runSideEffect(key: AnyHashable, action: (Lifetime) -> Void) { + func runSideEffect( + key: AnyHashable, + action: (Lifetime) -> Void + ) { if let existingSideEffect = originalSideEffectLifetimes[key] { usedSideEffectLifetimes[key] = existingSideEffect } else { diff --git a/Workflow/Sources/WorkflowAction.swift b/Workflow/Sources/WorkflowAction.swift index 9d7da058..27fdf6bd 100644 --- a/Workflow/Sources/WorkflowAction.swift +++ b/Workflow/Sources/WorkflowAction.swift @@ -45,7 +45,7 @@ public struct AnyWorkflowAction: WorkflowAction { /// Creates a type-erased workflow action that wraps the given instance. /// /// - Parameter base: A workflow action to wrap. - public init(_ base: E) where E: WorkflowAction, E.WorkflowType == WorkflowType { + public init(_ base: E) where E.WorkflowType == WorkflowType { if let anyEvent = base as? AnyWorkflowAction { self = anyEvent return diff --git a/Workflow/Sources/WorkflowObserver.swift b/Workflow/Sources/WorkflowObserver.swift index 6a812521..7bf50f36 100644 --- a/Workflow/Sources/WorkflowObserver.swift +++ b/Workflow/Sources/WorkflowObserver.swift @@ -276,11 +276,11 @@ final class ChainedWorkflowObserver: WorkflowObserver { } } - func workflowDidReceiveAction( + func workflowDidReceiveAction( _ action: Action, workflow: Action.WorkflowType, session: WorkflowSession - ) where Action: WorkflowAction { + ) { for observer in observers { observer.workflowDidReceiveAction( action, @@ -290,12 +290,12 @@ final class ChainedWorkflowObserver: WorkflowObserver { } } - func workflowWillApplyAction( + func workflowWillApplyAction( _ action: Action, workflow: Action.WorkflowType, state: Action.WorkflowType.State, session: WorkflowSession - ) -> ((Action.WorkflowType.State, Action.WorkflowType.Output?) -> Void)? where Action: WorkflowAction { + ) -> ((Action.WorkflowType.State, Action.WorkflowType.Output?) -> Void)? { let callbacks = observers.compactMap { $0.workflowWillApplyAction( action, diff --git a/Workflow/Tests/WorkflowObserverTests.swift b/Workflow/Tests/WorkflowObserverTests.swift index 2d511a77..26e3fa0a 100644 --- a/Workflow/Tests/WorkflowObserverTests.swift +++ b/Workflow/Tests/WorkflowObserverTests.swift @@ -647,11 +647,11 @@ private final class TestObserver: WorkflowObserver { onDidChange?(oldWorkflow, newWorkflow, state, session) } - func workflowDidReceiveAction(_ action: Action, workflow: Action.WorkflowType, session: WorkflowSession) where Action: WorkflowAction { + func workflowDidReceiveAction(_ action: Action, workflow: Action.WorkflowType, session: WorkflowSession) { onDidReceiveAction?(action, workflow, session) } - func workflowWillApplyAction(_ action: Action, workflow: Action.WorkflowType, state: Action.WorkflowType.State, session: WorkflowSession) -> ((Action.WorkflowType.State, Action.WorkflowType.Output?) -> Void)? where Action: WorkflowAction { + func workflowWillApplyAction(_ action: Action, workflow: Action.WorkflowType, state: Action.WorkflowType.State, session: WorkflowSession) -> ((Action.WorkflowType.State, Action.WorkflowType.Output?) -> Void)? { onApplyAction?(action, workflow, state, session) } } diff --git a/WorkflowTesting/Sources/Internal/RenderTester+TestContext.swift b/WorkflowTesting/Sources/Internal/RenderTester+TestContext.swift index 62ce67e8..0bb2583d 100644 --- a/WorkflowTesting/Sources/Internal/RenderTester+TestContext.swift +++ b/WorkflowTesting/Sources/Internal/RenderTester+TestContext.swift @@ -45,7 +45,7 @@ extension RenderTester { self.line = line } - func render(workflow: Child, key: String, outputMap: @escaping (Child.Output) -> Action) -> Child.Rendering where Child: Workflow, Action: WorkflowAction, Action.WorkflowType == WorkflowType { + func render(workflow: Child, key: String, outputMap: @escaping (Child.Output) -> Action) -> Child.Rendering where Action.WorkflowType == WorkflowType { let matchingTypes = expectedWorkflows.compactMap { $0 as? ExpectedWorkflow } guard let expectedWorkflow = matchingTypes.first(where: { $0.key == key }) else { let sameTypeDifferentKeys = matchingTypes.map(\.key) @@ -88,7 +88,7 @@ extension RenderTester { return expectedWorkflow.rendering } - func makeSink(of actionType: ActionType.Type) -> Sink where ActionType: WorkflowAction, ActionType.WorkflowType == WorkflowType { + func makeSink(of actionType: ActionType.Type) -> Sink where ActionType.WorkflowType == WorkflowType { Sink { action in self.apply(action: action) } @@ -114,7 +114,7 @@ extension RenderTester { } } - private func apply(action: ActionType) where ActionType: WorkflowAction, ActionType.WorkflowType == WorkflowType { + private func apply(action: ActionType) where ActionType.WorkflowType == WorkflowType { XCTAssertNil(appliedAction, "Received multiple actions in a single render test", file: file, line: line) appliedAction = AppliedAction(action) let output = action.apply(toState: &state) diff --git a/WorkflowTesting/Sources/WorkflowActionTester.swift b/WorkflowTesting/Sources/WorkflowActionTester.swift index ecc4bc33..7865b5e6 100644 --- a/WorkflowTesting/Sources/WorkflowActionTester.swift +++ b/WorkflowTesting/Sources/WorkflowActionTester.swift @@ -59,7 +59,7 @@ extension WorkflowAction { /// .assert(output: .finished) /// .assert(state: .differentState) /// ``` -public struct WorkflowActionTester where Action: WorkflowAction, Action.WorkflowType == WorkflowType { +public struct WorkflowActionTester where Action.WorkflowType == WorkflowType { /// The current state let state: WorkflowType.State let output: WorkflowType.Output? diff --git a/WorkflowTesting/Sources/WorkflowRenderTester.swift b/WorkflowTesting/Sources/WorkflowRenderTester.swift index 9846c60d..8cafc855 100644 --- a/WorkflowTesting/Sources/WorkflowRenderTester.swift +++ b/WorkflowTesting/Sources/WorkflowRenderTester.swift @@ -226,11 +226,11 @@ public struct RenderTester { /// - Parameters: /// - key: The key to expect. /// - action: The action to produce when this side-effect is requested. - public func expectSideEffect( + public func expectSideEffect( key: AnyHashable, producingAction action: ActionType, file: StaticString = #file, line: UInt = #line - ) -> RenderTester where ActionType: WorkflowAction, ActionType.WorkflowType == WorkflowType { + ) -> RenderTester where ActionType.WorkflowType == WorkflowType { RenderTester( workflow: workflow, state: state,