Skip to content

Commit 30334f9

Browse files
sk409kobayashi_shoto
andauthored
Support nested enum reducer (#2813) (#2814)
* Support nested enum reducer (#2813) * Support nested enum reducer without Macro (#2813) * Support nested enum reducer without Macro (#2813) * Support nested enum reducer with default initializer (#2813) * Minor fixes (#2813) * Minor fixes (#2813) --------- Co-authored-by: kobayashi_shoto <[email protected]>
1 parent ca0a0a9 commit 30334f9

File tree

3 files changed

+86
-4
lines changed

3 files changed

+86
-4
lines changed

Sources/ComposableArchitectureMacros/ReducerMacro.swift

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ private enum ReducerCase {
467467
let parameter = parameterClause.parameters.first,
468468
parameter.type.is(IdentifierTypeSyntax.self) || parameter.type.is(MemberTypeSyntax.self)
469469
{
470-
let stateCase = attribute == .ephemeral ? element : element.suffixed("State")
470+
let stateCase = attribute == .ephemeral ? element : element.suffixed("State").type
471471
return "case \(stateCase.trimmedDescription)"
472472
} else {
473473
return "case \(element.trimmedDescription)"
@@ -495,7 +495,7 @@ private enum ReducerCase {
495495
let parameter = parameterClause.parameters.first,
496496
parameter.type.is(IdentifierTypeSyntax.self) || parameter.type.is(MemberTypeSyntax.self)
497497
{
498-
return "case \(element.suffixed("Action").trimmedDescription)"
498+
return "case \(element.suffixed("Action").type.trimmedDescription)"
499499
} else {
500500
return "case \(element.name)(Swift.Never)"
501501
}
@@ -544,11 +544,12 @@ private enum ReducerCase {
544544
{
545545
let name = element.name.text
546546
let type = parameter.type
547+
let reducer = parameter.defaultValue?.value.trimmedDescription ?? "\(type.trimmed)()"
547548
return """
548549
ComposableArchitecture.Scope(\
549550
state: \\Self.State.Cases.\(name), action: \\Self.Action.Cases.\(name)\
550551
) {
551-
\(type.trimmed)()
552+
\(reducer)
552553
}
553554
"""
554555
} else {
@@ -750,12 +751,21 @@ extension EnumCaseDeclSyntax {
750751
}
751752

752753
extension EnumCaseElementSyntax {
754+
fileprivate var type: Self {
755+
var element = self
756+
if var parameterClause = element.parameterClause {
757+
parameterClause.parameters[parameterClause.parameters.startIndex].defaultValue = nil
758+
element.parameterClause = parameterClause
759+
}
760+
return element
761+
}
762+
753763
fileprivate func suffixed(_ suffix: TokenSyntax) -> Self {
754764
var element = self
755765
if var parameterClause = element.parameterClause,
756766
let type = parameterClause.parameters.first?.type
757767
{
758-
let type = MemberTypeSyntax(baseType: type, name: suffix)
768+
let type = MemberTypeSyntax(baseType: type.trimmed, name: suffix)
759769
parameterClause.parameters[parameterClause.parameters.startIndex].type = TypeSyntax(type)
760770
element.parameterClause = parameterClause
761771
}

Tests/ComposableArchitectureMacrosTests/ReducerMacroTests.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,67 @@
340340
"""#
341341
}
342342
}
343+
344+
func testEnum_DefaultInitializer() {
345+
assertMacro {
346+
"""
347+
@Reducer
348+
enum Destination {
349+
case timeline(Timeline)
350+
case meeting(Meeting = Meeting(context: .sheet))
351+
}
352+
"""
353+
} expansion: {
354+
#"""
355+
enum Destination {
356+
case timeline(Timeline)
357+
case meeting(Meeting = Meeting(context: .sheet))
358+
359+
@CasePathable
360+
@dynamicMemberLookup
361+
@ObservableState
362+
enum State: ComposableArchitecture.CaseReducerState {
363+
typealias StateReducer = Destination
364+
case timeline(Timeline.State)
365+
case meeting(Meeting.State)
366+
}
367+
368+
@CasePathable
369+
enum Action {
370+
case timeline(Timeline.Action)
371+
case meeting(Meeting.Action)
372+
}
373+
374+
@ComposableArchitecture.ReducerBuilder<Self.State, Self.Action>
375+
static var body: ComposableArchitecture.ReducerBuilder<Self.State, Self.Action>._Sequence<ComposableArchitecture.Scope<Self.State, Self.Action, Timeline>, ComposableArchitecture.Scope<Self.State, Self.Action, Meeting>> {
376+
ComposableArchitecture.Scope(state: \Self.State.Cases.timeline, action: \Self.Action.Cases.timeline) {
377+
Timeline()
378+
}
379+
ComposableArchitecture.Scope(state: \Self.State.Cases.meeting, action: \Self.Action.Cases.meeting) {
380+
Meeting(context: .sheet)
381+
}
382+
}
383+
384+
enum CaseScope {
385+
case timeline(ComposableArchitecture.StoreOf<Timeline>)
386+
case meeting(ComposableArchitecture.StoreOf<Meeting>)
387+
}
388+
389+
static func scope(_ store: ComposableArchitecture.Store<Self.State, Self.Action>) -> CaseScope {
390+
switch store.state {
391+
case .timeline:
392+
return .timeline(store.scope(state: \.timeline, action: \.timeline)!)
393+
case .meeting:
394+
return .meeting(store.scope(state: \.meeting, action: \.meeting)!)
395+
}
396+
}
397+
}
398+
399+
extension Destination: ComposableArchitecture.CaseReducer, ComposableArchitecture.Reducer {
400+
}
401+
"""#
402+
}
403+
}
343404

344405
func testEnum_Empty() {
345406
assertMacro {

Tests/ComposableArchitectureTests/EnumReducerMacroTests.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,15 @@
2828
#endif
2929
}
3030
}
31+
32+
private enum TestEnumReducer_DefaultInitializer {
33+
@Reducer
34+
struct Feature {
35+
let context: String
36+
}
37+
@Reducer
38+
enum Destination1 {
39+
case feature1(Feature = Feature(context: "context"))
40+
}
41+
}
3142
#endif

0 commit comments

Comments
 (0)