From 3ab2a992e94b961dd9796c576daec783f0d581c6 Mon Sep 17 00:00:00 2001 From: mohamad reza koohkan Date: Wed, 10 Jul 2024 15:03:19 +0300 Subject: [PATCH 1/2] Explicit type name --- .../SealedMacroPlayground.swift | 8 +-- Sources/PlaytomicMacrosSource/Helper.swift | 30 ++++++++ .../attached/SealedMacro.swift | 70 ++++++++----------- 3 files changed, 63 insertions(+), 45 deletions(-) diff --git a/Sources/PlaytomicMacrosClient/SealedMacroPlayground.swift b/Sources/PlaytomicMacrosClient/SealedMacroPlayground.swift index f3f6e2d..bf80de3 100644 --- a/Sources/PlaytomicMacrosClient/SealedMacroPlayground.swift +++ b/Sources/PlaytomicMacrosClient/SealedMacroPlayground.swift @@ -74,7 +74,7 @@ private func handleViewAction(_ action: LevelUpgradeViewAction) -> String { // Handling specific action types private func handleNavigationAction(_ action: LevelUpgradeNavigationAction) -> String { - switch action.type { + switch action.navigationType { case let .OnUpgradeSuccess(action): "Navigation: Upgrade success from \(action.from) to \(action.to)" case let .OnUpgradeError(action): "Navigation: Upgrade error: \(action.error)" case .OnUpgradeSkipped: "Navigation: Upgrade skipped" @@ -82,7 +82,7 @@ private func handleNavigationAction(_ action: LevelUpgradeNavigationAction) -> S } private func handleEventAction(_ action: LevelUpgradeEventAction) -> String { - switch action.type { + switch action.eventType { case .OnAppear: "Event: On appear" case let .OnUpgradeStarted(action): "Event: Upgrade started from \(action.from) to \(action.to)" case .OnUpgradeSkipped: "Event: Upgrade skipped" @@ -98,6 +98,6 @@ func runSealedMacroPlayground() { let y: LevelUpgradeEventAction = LevelUpgradeViewAction.OnAppear() let z: LevelUpgradeNavigationAction = LevelUpgradeViewAction.OnUpgradeSkipped() print(x.type) - print(y.type) - print(z.type) + print(x.navigationType) + print(x.eventType) } diff --git a/Sources/PlaytomicMacrosSource/Helper.swift b/Sources/PlaytomicMacrosSource/Helper.swift index c54adf6..6bf38bf 100644 --- a/Sources/PlaytomicMacrosSource/Helper.swift +++ b/Sources/PlaytomicMacrosSource/Helper.swift @@ -47,3 +47,33 @@ extension ClassDeclSyntax { } != nil } } + +extension String { + var wordsSeparatedByCapitalLetter: [String] { + self.lazy + .map({ $0.isUppercase ? " \($0)" : "\($0)" }) + .joined() + .trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) + .components(separatedBy: " ") + } + + func diff(_ rhs: String) -> [String] { + let lhsWords = wordsSeparatedByCapitalLetter + let rhsWords = rhs.wordsSeparatedByCapitalLetter + var differences: [String] = [] + + let minLength = min(lhsWords.count, rhsWords.count) + + for i in 0.. minLength { + differences.append(contentsOf: rhsWords[minLength...]) + } + + return differences + } +} diff --git a/Sources/PlaytomicMacrosSource/attached/SealedMacro.swift b/Sources/PlaytomicMacrosSource/attached/SealedMacro.swift index dbfde6e..0f7fefb 100644 --- a/Sources/PlaytomicMacrosSource/attached/SealedMacro.swift +++ b/Sources/PlaytomicMacrosSource/attached/SealedMacro.swift @@ -77,7 +77,7 @@ public enum SealedMacro: ExtensionMacro { let sealedTypesSynax = sealedTypes.keys.map { inheritedClassName in generateEnumType( - typeName: inheritedClassName, + typeName: typeIdentifier.diff(inheritedClassName + SEALED_TYPE_SUFFIX).joined(), onGenerateCases: { generateEnumCases( parenTypeName: typeIdentifier, @@ -87,38 +87,25 @@ public enum SealedMacro: ExtensionMacro { ) }.joined(separator: "\n\n").appending("\n") -// let typeProperty = generateTypeProperty( -// parenTypeName: typeIdentifier, -// allCases: sealedTypes[typeIdentifier] ?? [] -// ).appending("\n") - let sealedTypesDefinitionExtensionSyntax = try ExtensionDeclSyntax( """ extension \(type.trimmed) { \(raw: sealedTypesSynax) } """ - ) - -// let sealedTypesDefinitionExtensionSyntax = try ExtensionDeclSyntax( -// """ -// extension \(type.trimmed) { -// \(raw: sealedTypesSynax) -// \(raw: typeProperty) -// } -// """ -// ) + ) let sealedTypesResolutionExtensionSyntaxes = try sealedTypes.keys .sorted(by: { lhs, rhs in lhs == typeIdentifier }) .map { sealedType in - try generateSealedTypesExtensions( - parenTypeName: typeIdentifier, - subActionTypeName: sealedType, - allCases: sealedTypes[sealedType] ?? [] - ) - } + try generateSealedTypesExtensions( + parenTypeName: typeIdentifier, + subTypeName: sealedType, + subActionTypeName: typeIdentifier.diff(sealedType).joined(), + allCases: sealedTypes[sealedType] ?? [] + ) + } return [sealedTypesDefinitionExtensionSyntax] + sealedTypesResolutionExtensionSyntaxes } @@ -128,7 +115,7 @@ public enum SealedMacro: ExtensionMacro { onGenerateCases: @escaping () -> String ) -> String { """ - enum \(typeName)\(SEALED_TYPE_SUFFIX) { + enum \(typeName) { \(onGenerateCases()) } """ @@ -142,25 +129,26 @@ public enum SealedMacro: ExtensionMacro { .joined(separator: "\n") } - private static func generateTypeProperty(parenTypeName: String, allCases: [ClassDeclSyntax]) -> String { - let allCasesSyntax = allCases.map { eachCase in - let name = eachCase.name.text - return "case is \(parenTypeName).\(name): \(parenTypeName)\(SEALED_TYPE_SUFFIX).\(name)\(eachCase.isContainProperty ? "(self as! \(parenTypeName).\(name))" : "")" - }.joined(separator: "\n") - - return """ - var type: \(parenTypeName)\(SEALED_TYPE_SUFFIX) { - switch self { - \(allCasesSyntax) - default: fatalError("Unknown subclass") - } - } - """ - } +// private static func generateTypeProperty(parenTypeName: String, allCases: [ClassDeclSyntax]) -> String { +// let allCasesSyntax = allCases.map { eachCase in +// let name = eachCase.name.text +// return "case is \(parenTypeName).\(name): \(parenTypeName)\(SEALED_TYPE_SUFFIX).\(name)\(eachCase.isContainProperty ? "(self as! \(parenTypeName).\(name))" : "")" +// }.joined(separator: "\n") +// +// return """ +// var type: \(parenTypeName)\(SEALED_TYPE_SUFFIX) { +// switch self { +// \(allCasesSyntax) +// default: fatalError("Unknown subclass") +// } +// } +// """ +// } private static func generateSealedTypesExtensions( parenTypeName: String, + subTypeName: String, subActionTypeName: String, allCases: [ClassDeclSyntax] ) throws -> ExtensionDeclSyntax { @@ -171,11 +159,11 @@ public enum SealedMacro: ExtensionMacro { return try ExtensionDeclSyntax( """ - extension \(raw: subActionTypeName) { - var type: \(raw: parenTypeName).\(raw: subActionTypeName)\(raw: SEALED_TYPE_SUFFIX) { + extension \(raw: subTypeName) { + var \(subActionTypeName.isEmpty ? "type" : "\(raw: subActionTypeName.lowercased())Type"): \(raw: parenTypeName).\(raw: subActionTypeName)\(raw: SEALED_TYPE_SUFFIX) { switch self { \(raw: allCasesSyntax) - default: fatalError("Unknown \(raw: subActionTypeName) type") + default: fatalError("Unknown \(raw: subTypeName) type") } } } From 0e8ffa7fc855fa009d30e8ac01aa9890ea5d99fc Mon Sep 17 00:00:00 2001 From: mohamad reza koohkan Date: Wed, 10 Jul 2024 16:59:49 +0300 Subject: [PATCH 2/2] All in one macro --- .../AddAsyncMacroPlayground.swift | 2 + .../CaseDetectionMacroPlayground.swift | 2 + .../CopyableMacroPlayground.swift | 2 + .../EquatableMacroPlayground.swift | 2 + Sources/PlaytomicMacrosClient/Helper.swift | 12 +++++ .../SealedMacroPlayground.swift | 47 ++++++++++++------- .../StoredAccessMacroPlayground.swift | 3 +- .../StringifyMacroPlayground.swift | 2 + .../URLMacroPlayground.swift | 2 + .../WarningMacroPlayground.swift | 2 + .../WrapStoredPropertiesMacroPlayground.swift | 3 +- .../attached/SealedMacro.swift | 33 ++++++------- 12 files changed, 75 insertions(+), 37 deletions(-) diff --git a/Sources/PlaytomicMacrosClient/AddAsyncMacroPlayground.swift b/Sources/PlaytomicMacrosClient/AddAsyncMacroPlayground.swift index d2a0ed6..f5560d8 100644 --- a/Sources/PlaytomicMacrosClient/AddAsyncMacroPlayground.swift +++ b/Sources/PlaytomicMacrosClient/AddAsyncMacroPlayground.swift @@ -14,6 +14,8 @@ func sportsSelector(sports: [String], callback: @escaping (String) -> Void) -> V } func runAddAsyncMacroPlayground() async { + startRunner() + defer { stopRunner() } let sport = await sportsSelector(sports: ["Padel", "Tenis", "Pickleball"]) print("@addAsyncMacro:", sport) } diff --git a/Sources/PlaytomicMacrosClient/CaseDetectionMacroPlayground.swift b/Sources/PlaytomicMacrosClient/CaseDetectionMacroPlayground.swift index 6d8ba25..e8961df 100644 --- a/Sources/PlaytomicMacrosClient/CaseDetectionMacroPlayground.swift +++ b/Sources/PlaytomicMacrosClient/CaseDetectionMacroPlayground.swift @@ -15,6 +15,8 @@ enum ProfileViewState { } func runCaseDetectionMacroPlayground() { + startRunner() + defer { stopRunner() } var state = ProfileViewState.loading print("@caseDetection: is the view state loading?: \(state.isLoading)") state = ProfileViewState.loaded(userName: "Random name") diff --git a/Sources/PlaytomicMacrosClient/CopyableMacroPlayground.swift b/Sources/PlaytomicMacrosClient/CopyableMacroPlayground.swift index 7642406..c38c624 100644 --- a/Sources/PlaytomicMacrosClient/CopyableMacroPlayground.swift +++ b/Sources/PlaytomicMacrosClient/CopyableMacroPlayground.swift @@ -20,6 +20,8 @@ struct ExampleViewState { } func runCopyableMacroPlayground() { + startRunner() + defer { stopRunner() } let x = ExampleViewState(title: "1", count: 2) print("@Copyable:", x) print("@Copyable:", x.copy(title: "2", count: .value(nil))) diff --git a/Sources/PlaytomicMacrosClient/EquatableMacroPlayground.swift b/Sources/PlaytomicMacrosClient/EquatableMacroPlayground.swift index 831103c..a0abf31 100644 --- a/Sources/PlaytomicMacrosClient/EquatableMacroPlayground.swift +++ b/Sources/PlaytomicMacrosClient/EquatableMacroPlayground.swift @@ -21,6 +21,8 @@ struct MatchTeam { } func runEquatableMacroPlayground() { + startRunner() + defer { stopRunner() } let id1 = UUID().uuidString let match1 = Match(id: id1, team: MatchTeam(id: id1, playerd: [])) let match2 = Match(id: id1, team: MatchTeam(id: id1, playerd: [])) diff --git a/Sources/PlaytomicMacrosClient/Helper.swift b/Sources/PlaytomicMacrosClient/Helper.swift index a398219..d5b2d65 100644 --- a/Sources/PlaytomicMacrosClient/Helper.swift +++ b/Sources/PlaytomicMacrosClient/Helper.swift @@ -26,3 +26,15 @@ func ?? (_ nullable: Nullable, _ defValue: T?) -> T? { nullable.or(defValue) } +func startRunner(file: String = #file) { + let fileNameWithExtension = URL(string: file)!.lastPathComponent + let fileNameWithoutExtension = fileNameWithExtension.replacingOccurrences(of: ".swift", with: "") + print("\n### --- RUNNING \(fileNameWithoutExtension) --- ###\n") +} + +func stopRunner(file: String = #file, function: String = #function) { + let fileNameWithExtension = URL(string: file)!.lastPathComponent + let fileNameWithoutExtension = fileNameWithExtension.replacingOccurrences(of: ".swift", with: "") + print("\n### --- FINISHED \(fileNameWithoutExtension) --- ###\n") +} + diff --git a/Sources/PlaytomicMacrosClient/SealedMacroPlayground.swift b/Sources/PlaytomicMacrosClient/SealedMacroPlayground.swift index bf80de3..44430e0 100644 --- a/Sources/PlaytomicMacrosClient/SealedMacroPlayground.swift +++ b/Sources/PlaytomicMacrosClient/SealedMacroPlayground.swift @@ -61,8 +61,8 @@ class LevelUpgradeViewAction: ViewAction { // Usage example -private func handleViewAction(_ action: LevelUpgradeViewAction) -> String { - switch action.type { +private func handleViewAction(_ action: LevelUpgradeViewAction.SealedType) -> String { + switch action { case .OnAppear: "On appear" case .Upgrade: "Upgrade" case let .OnUpgradeStarted(action): "Upgrade started from \(action.from) to \(action.to)" @@ -73,31 +73,46 @@ private func handleViewAction(_ action: LevelUpgradeViewAction) -> String { } // Handling specific action types -private func handleNavigationAction(_ action: LevelUpgradeNavigationAction) -> String { - switch action.navigationType { +private func handleNavigationAction(_ action: LevelUpgradeViewAction.NavigationSealedType) -> String { + switch action { case let .OnUpgradeSuccess(action): "Navigation: Upgrade success from \(action.from) to \(action.to)" case let .OnUpgradeError(action): "Navigation: Upgrade error: \(action.error)" case .OnUpgradeSkipped: "Navigation: Upgrade skipped" } } -private func handleEventAction(_ action: LevelUpgradeEventAction) -> String { - switch action.eventType { +private func handleEventAction(_ action: LevelUpgradeViewAction.EventSealedType) -> String { + switch action { case .OnAppear: "Event: On appear" case let .OnUpgradeStarted(action): "Event: Upgrade started from \(action.from) to \(action.to)" case .OnUpgradeSkipped: "Event: Upgrade skipped" } } +private func handle(_ action: LevelUpgradeViewAction) { + if let viewAction = action.type { + print("VIEW:", handleViewAction(viewAction)) + } + + if let navigationAction = action.navigationType { + print("NAVIGATION:", handleNavigationAction(navigationAction)) + } + + if let eventAction = action.eventType { + print("EVENT:", handleEventAction(eventAction)) + } +} + func runSealedMacroPlayground() { - print(handleViewAction(LevelUpgradeViewAction.OnUpgradeStarted(from: 1.0, to: 2.0))) - print(handleNavigationAction(LevelUpgradeViewAction.OnUpgradeSuccess(from: 1.0, to: 2.0))) - print(handleEventAction(LevelUpgradeViewAction.OnUpgradeStarted(from: 1.0, to: 2.0))) - - let x: LevelUpgradeViewAction = LevelUpgradeViewAction.OnAppear() - let y: LevelUpgradeEventAction = LevelUpgradeViewAction.OnAppear() - let z: LevelUpgradeNavigationAction = LevelUpgradeViewAction.OnUpgradeSkipped() - print(x.type) - print(x.navigationType) - print(x.eventType) + startRunner() + defer { stopRunner() } + handle(LevelUpgradeViewAction.OnUpgradeStarted(from: 1.0, to: 2.0)) + handle(LevelUpgradeViewAction.OnUpgradeSuccess(from: 1.0, to: 2.0)) + handle(LevelUpgradeViewAction.OnUpgradeStarted(from: 1.0, to: 2.0)) +// let x: LevelUpgradeViewAction = LevelUpgradeViewAction.OnAppear() +// let y: LevelUpgradeEventAction = LevelUpgradeViewAction.OnAppear() +// let z: LevelUpgradeNavigationAction = LevelUpgradeViewAction.OnUpgradeSkipped() +// print(x.type) +// print(x.navigationType) +// print(x.eventType) } diff --git a/Sources/PlaytomicMacrosClient/StoredAccessMacroPlayground.swift b/Sources/PlaytomicMacrosClient/StoredAccessMacroPlayground.swift index 1cf2803..039e33b 100644 --- a/Sources/PlaytomicMacrosClient/StoredAccessMacroPlayground.swift +++ b/Sources/PlaytomicMacrosClient/StoredAccessMacroPlayground.swift @@ -14,5 +14,6 @@ struct User { } func runStoredAccessMacroPlayground() { - + startRunner() + defer { stopRunner() } } diff --git a/Sources/PlaytomicMacrosClient/StringifyMacroPlayground.swift b/Sources/PlaytomicMacrosClient/StringifyMacroPlayground.swift index ea8e09d..87af3d5 100644 --- a/Sources/PlaytomicMacrosClient/StringifyMacroPlayground.swift +++ b/Sources/PlaytomicMacrosClient/StringifyMacroPlayground.swift @@ -9,6 +9,8 @@ import Foundation import PlaytomicMacros func runStringifyMacroPlayground() { + startRunner() + defer { stopRunner() } let a = 17 let b = 25 let (result, code) = #stringify(a + b) diff --git a/Sources/PlaytomicMacrosClient/URLMacroPlayground.swift b/Sources/PlaytomicMacrosClient/URLMacroPlayground.swift index 598d3f6..1812f18 100644 --- a/Sources/PlaytomicMacrosClient/URLMacroPlayground.swift +++ b/Sources/PlaytomicMacrosClient/URLMacroPlayground.swift @@ -13,5 +13,7 @@ import PlaytomicMacros malformed an error is emitted. Otherwise a non-optional URL is expanded. */ func runURLMacroPlayground() { + startRunner() + defer { stopRunner() } print(#URL("https://playtomic.io/")) } diff --git a/Sources/PlaytomicMacrosClient/WarningMacroPlayground.swift b/Sources/PlaytomicMacrosClient/WarningMacroPlayground.swift index 1fa74b9..3e1a4db 100644 --- a/Sources/PlaytomicMacrosClient/WarningMacroPlayground.swift +++ b/Sources/PlaytomicMacrosClient/WarningMacroPlayground.swift @@ -9,5 +9,7 @@ import Foundation import PlaytomicMacros func runWarningMacroPlayground() { + startRunner() + defer { stopRunner() } #warning("This macro generates a message") } diff --git a/Sources/PlaytomicMacrosClient/WrapStoredPropertiesMacroPlayground.swift b/Sources/PlaytomicMacrosClient/WrapStoredPropertiesMacroPlayground.swift index b8480cf..d4a07cd 100644 --- a/Sources/PlaytomicMacrosClient/WrapStoredPropertiesMacroPlayground.swift +++ b/Sources/PlaytomicMacrosClient/WrapStoredPropertiesMacroPlayground.swift @@ -14,5 +14,6 @@ struct OldStorage { } func runWrapStoredPropertiesMacroPlayground() { - + startRunner() + defer { stopRunner() } } diff --git a/Sources/PlaytomicMacrosSource/attached/SealedMacro.swift b/Sources/PlaytomicMacrosSource/attached/SealedMacro.swift index 0f7fefb..9f1481d 100644 --- a/Sources/PlaytomicMacrosSource/attached/SealedMacro.swift +++ b/Sources/PlaytomicMacrosSource/attached/SealedMacro.swift @@ -29,13 +29,6 @@ public enum SealedMacro: ExtensionMacro { conformingTo protocols: [TypeSyntax], in context: some MacroExpansionContext ) throws -> [ExtensionDeclSyntax] { - // guard declaration.as(StructDeclSyntax.self) != nil || declaration.as(ClassDeclSyntax.self) != nil, - // let name = declaration.as(StructDeclSyntax.self)?.name ?? declaration.as(ClassDeclSyntax.self)?.name else { - // let diagnostic = Diagnostic(node: node, message: CopyableMacroDiagnostic.notAStructOrClass) - // context.diagnose(diagnostic) - // return [] - // } - guard let typeIdentifier = type.as(IdentifierTypeSyntax.self)?.name.trimmed.text else { return [] @@ -73,8 +66,6 @@ public enum SealedMacro: ExtensionMacro { } } - print("PRINT<<", sealedTypes) - let sealedTypesSynax = sealedTypes.keys.map { inheritedClassName in generateEnumType( typeName: typeIdentifier.diff(inheritedClassName + SEALED_TYPE_SUFFIX).joined(), @@ -105,9 +96,16 @@ public enum SealedMacro: ExtensionMacro { subActionTypeName: typeIdentifier.diff(sealedType).joined(), allCases: sealedTypes[sealedType] ?? [] ) - } + }.joined(separator: "\n\n") - return [sealedTypesDefinitionExtensionSyntax] + sealedTypesResolutionExtensionSyntaxes + return [try ExtensionDeclSyntax( + """ + extension \(type.trimmed) { + \(raw: sealedTypesSynax) + \(raw: sealedTypesResolutionExtensionSyntaxes) + } + """ + )] } private static func generateEnumType( @@ -151,24 +149,21 @@ public enum SealedMacro: ExtensionMacro { subTypeName: String, subActionTypeName: String, allCases: [ClassDeclSyntax] - ) throws -> ExtensionDeclSyntax { + ) -> String { let allCasesSyntax = allCases.map { eachCase in let name = eachCase.name.text return "case is \(parenTypeName).\(name): \(parenTypeName).\(subActionTypeName)\(SEALED_TYPE_SUFFIX).\(name)\(eachCase.isContainProperty ? "(self as! \(parenTypeName).\(name))" : "")" }.joined(separator: "\n") - return try ExtensionDeclSyntax( + return """ - extension \(raw: subTypeName) { - var \(subActionTypeName.isEmpty ? "type" : "\(raw: subActionTypeName.lowercased())Type"): \(raw: parenTypeName).\(raw: subActionTypeName)\(raw: SEALED_TYPE_SUFFIX) { + var \(subActionTypeName.isEmpty ? "type" : "\(subActionTypeName.lowercased())Type"): \(parenTypeName).\(subActionTypeName)\(SEALED_TYPE_SUFFIX)? { switch self { - \(raw: allCasesSyntax) - default: fatalError("Unknown \(raw: subTypeName) type") - } + \(allCasesSyntax) + default: nil } } """ - ) } }