diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/TCADiagram-Package.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/TCADiagram-Package.xcscheme
new file mode 100644
index 0000000..b9885e2
--- /dev/null
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/TCADiagram-Package.xcscheme
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/tca-diagram-lib.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/tca-diagram-lib.xcscheme
new file mode 100644
index 0000000..d4edfda
--- /dev/null
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/tca-diagram-lib.xcscheme
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/tca-diagram.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/tca-diagram.xcscheme
new file mode 100644
index 0000000..c745657
--- /dev/null
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/tca-diagram.xcscheme
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Sources/TCADiagram/TCADiagram.swift b/Sources/TCADiagram/TCADiagram.swift
index b6e6a2d..3f3ea9d 100644
--- a/Sources/TCADiagram/TCADiagram.swift
+++ b/Sources/TCADiagram/TCADiagram.swift
@@ -8,7 +8,7 @@ import TCADiagramLib
struct TCADiagram: ParsableCommand {
static var configuration: CommandConfiguration = .init(
commandName: "tca-diagram",
- version: "0.1.1"
+ version: "0.2.0"
)
@Option(name: .shortAndLong, help: "Root directory of swift files")
diff --git a/Sources/TCADiagramLib/Internal/Parser.swift b/Sources/TCADiagramLib/Internal/Parser.swift
index b87a5e7..db4acdb 100644
--- a/Sources/TCADiagramLib/Internal/Parser.swift
+++ b/Sources/TCADiagramLib/Internal/Parser.swift
@@ -14,6 +14,8 @@ extension SourceFileSyntax {
optional: isOptionalPullback(node)
)
)
+ } else if false {
+ // TODO: predicateReducerProtocol
} else if let name = try predicateActionDecl(node) {
actions.insert(name)
} else {
@@ -37,20 +39,61 @@ extension SourceFileSyntax {
private func predicatePullbackCall(_ node: Syntax) throws -> (FunctionCallExprSyntax, String, String)? {
if
let node = FunctionCallExprSyntax(node),
- let action = node.argumentList.first(where: { syntax in syntax.label?.text == "action" })?.expression,
- let child = node.description.firstMatch(of: try Regex("\\s+(.+?)Reducer"))?[1].substring?.description,
- let parent = "\(action)".firstMatch(of: try Regex("\\/(.+?)Action.+"))?[1].substring?.description,
- node.tokens(viewMode: .fixedUp).map(\.text).contains("pullback")
+ let action = node.argumentList.first(where: { syntax in syntax.label?.text == "action" })?.expression
{
- return (node, parent, child)
+ let child = node.description.firstMatch(of: try Regex("\\s+(.+?)Reducer"))?[1].substring?.description
+ let parent = "\(action)".firstMatch(of: try Regex("\\/(.+?)Action.+"))?[1].substring?.description
+ switch (child, parent) {
+ case (.some("Any"), .some(let parent)):
+ if
+ let child = node.description
+ .firstMatch(of: try Regex("(?s)\\s+AnyReducer\\s+\\{.+?in\\s+(.+?)\\("))?[1]
+ .substring?
+ .description,
+ node.tokens(viewMode: .fixedUp).map(\.text).contains("pullback")
+ {
+ return (node, parent, child)
+ }
+ return .none
+
+ case (.some(let child), .some(let parent)):
+ if node.tokens(viewMode: .fixedUp).map(\.text).contains("pullback") {
+ return (node, parent, child)
+ }
+ return .none
+
+ default:
+ return .none
+ }
}
return .none
}
/// `enum`으로 정의된 액션을 찾아 피쳐 이름을 가져옵니다.
private func predicateActionDecl(_ node: Syntax) throws -> String? {
- if let node = EnumDeclSyntax(node), node.identifier.text.hasSuffix("Action") {
- return node.identifier.text.replacing("Action", with: "")
+ if let node = EnumDeclSyntax(node) {
+ if node.identifier.text == "Action" {
+ var parent = node.parent
+ while parent != nil {
+ if
+ let ext = ExtensionDeclSyntax(parent),
+ let name = ext.children(viewMode: .fixedUp)
+ .compactMap(SimpleTypeIdentifierSyntax.init)
+ .first?
+ .name
+ .text
+ {
+ return name
+ } else {
+ parent = parent?.parent
+ }
+ }
+ return .none
+ } else if node.identifier.text.hasSuffix("Action") {
+ return node.identifier.text.replacing("Action", with: "")
+ } else {
+ return .none
+ }
}
return .none
}
diff --git a/Tests/TCADiagramLibTests/DiagramTests.swift b/Tests/TCADiagramLibTests/DiagramTests.swift
index 8ec598f..8c7019e 100644
--- a/Tests/TCADiagramLibTests/DiagramTests.swift
+++ b/Tests/TCADiagramLibTests/DiagramTests.swift
@@ -10,6 +10,7 @@ final class DiagramTests: XCTestCase {
```mermaid
%%{ init : { "theme" : "default", "flowchart" : { "curve" : "monotoneY" }}}%%
graph LR
+ EmailSignUp ---> SignUpAgreement
SelfLessonDetail ---> Payment
SelfLessonDetail -- optional --> SantaWeb
SelfLessonDetail -- optional --> SelfLessonDetailFilter
@@ -17,6 +18,7 @@ final class DiagramTests: XCTestCase {
Payment(Payment: 1)
SantaWeb(SantaWeb: 1)
SelfLessonDetailFilter(SelfLessonDetailFilter: 1)
+ SignUpAgreement(SignUpAgreement: 1)
```
"""
XCTAssertEqual(result, expected)
diff --git a/Tests/TCADiagramLibTests/Resources/Sources.swift b/Tests/TCADiagramLibTests/Resources/Sources.swift
index 744e619..973ff5d 100644
--- a/Tests/TCADiagramLibTests/Resources/Sources.swift
+++ b/Tests/TCADiagramLibTests/Resources/Sources.swift
@@ -45,5 +45,32 @@ let sources: [String] = [
}
enum SelfLessonDetailFilterAction: Equatable {
}
+ """,
"""
+ public let emailSignUpReducer = EmailSignUpReducer
+ .combine(
+ AnyReducer { _ in
+ SignUpAgreement()
+ }
+ .pullback(
+ state: \\.signUpAgreement,
+ action: /EmailSignUpAction.signUpAgreement,
+ environment: { _ in }
+ ),
+ .init { state, action, environment in
+ switch action {
+ default:
+ return .none
+ }
+ }
+ )
+ """,
+ """
+ enum EmailSignUpAction {
+ }
+ extension SignUpAgreement {
+ public enum Action: Equatable {
+ }
+ }
+ """,
]