-
Notifications
You must be signed in to change notification settings - Fork 130
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Swift macro (requires Swift v5.9) (#58)
* Add Swift macro (requires Swift v5.9) * Update Swift workflow * Fix expandable section in readme * Use initializers * Throw error instead of returning empty array * Do not abbreviate enumeration * Update file header comments * Fix tests * Utilize transform instead of for loop * Remove local variables * Improve formatting * Use throwing initializer
- Loading branch information
1 parent
0f79642
commit 00ee8f4
Showing
12 changed files
with
371 additions
and
126 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,41 @@ | ||
{ | ||
"object": { | ||
"pins": [ | ||
{ | ||
"package": "CwlCatchException", | ||
"repositoryURL": "https://github.com/mattgallagher/CwlCatchException.git", | ||
"state": { | ||
"branch": null, | ||
"revision": "682841464136f8c66e04afe5dbd01ab51a3a56f2", | ||
"version": "2.1.0" | ||
} | ||
}, | ||
{ | ||
"package": "CwlPreconditionTesting", | ||
"repositoryURL": "https://github.com/mattgallagher/CwlPreconditionTesting.git", | ||
"state": { | ||
"branch": null, | ||
"revision": "02b7a39a99c4da27abe03cab2053a9034379639f", | ||
"version": "2.0.0" | ||
} | ||
}, | ||
{ | ||
"package": "Nimble", | ||
"repositoryURL": "https://github.com/Quick/Nimble.git", | ||
"state": { | ||
"branch": null, | ||
"revision": "af1730dde4e6c0d45bf01b99f8a41713ce536790", | ||
"version": "9.2.0" | ||
} | ||
"pins" : [ | ||
{ | ||
"identity" : "cwlcatchexception", | ||
"kind" : "remoteSourceControl", | ||
"location" : "https://github.com/mattgallagher/CwlCatchException.git", | ||
"state" : { | ||
"revision" : "3b123999de19bf04905bc1dfdb76f817b0f2cc00", | ||
"version" : "2.1.2" | ||
} | ||
] | ||
}, | ||
"version": 1 | ||
}, | ||
{ | ||
"identity" : "cwlpreconditiontesting", | ||
"kind" : "remoteSourceControl", | ||
"location" : "https://github.com/mattgallagher/CwlPreconditionTesting.git", | ||
"state" : { | ||
"revision" : "dc9af4781f2afdd1e68e90f80b8603be73ea7abc", | ||
"version" : "2.2.0" | ||
} | ||
}, | ||
{ | ||
"identity" : "nimble", | ||
"kind" : "remoteSourceControl", | ||
"location" : "https://github.com/Quick/Nimble.git", | ||
"state" : { | ||
"revision" : "efe11bbca024b57115260709b5c05e01131470d0", | ||
"version" : "13.2.1" | ||
} | ||
}, | ||
{ | ||
"identity" : "swift-syntax", | ||
"kind" : "remoteSourceControl", | ||
"location" : "https://github.com/apple/swift-syntax.git", | ||
"state" : { | ||
"revision" : "64889f0c732f210a935a0ad7cda38f77f876262d", | ||
"version" : "509.1.1" | ||
} | ||
} | ||
], | ||
"version" : 2 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// swift-tools-version:5.9 | ||
|
||
import PackageDescription | ||
import CompilerPluginSupport | ||
|
||
let package = Package( | ||
name: "StateMachine", | ||
platforms: [ | ||
.macOS(.v10_15), | ||
.iOS(.v13), | ||
.tvOS(.v13), | ||
.watchOS(.v5), | ||
], | ||
products: [ | ||
.library( | ||
name: "StateMachine", | ||
targets: ["StateMachine"]), | ||
], | ||
dependencies: [ | ||
.package( | ||
url: "https://github.com/apple/swift-syntax.git", | ||
from: "509.1.0"), | ||
.package( | ||
url: "https://github.com/Quick/Nimble.git", | ||
from: "13.2.0"), | ||
], | ||
targets: [ | ||
.target( | ||
name: "StateMachine", | ||
dependencies: ["StateMachineMacros"], | ||
path: "Swift/Sources/StateMachine"), | ||
.macro( | ||
name: "StateMachineMacros", | ||
dependencies: [ | ||
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"), | ||
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"), | ||
], | ||
path: "Swift/Sources/StateMachineMacros"), | ||
.testTarget( | ||
name: "StateMachineTests", | ||
dependencies: [ | ||
"StateMachine", | ||
"StateMachineMacros", | ||
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"), | ||
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"), | ||
"Nimble", | ||
], | ||
path: "Swift/Tests/StateMachineTests"), | ||
] | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// | ||
// Copyright (c) 2024, Match Group, LLC | ||
// BSD License, see LICENSE file for details | ||
// | ||
|
||
@attached(extension, | ||
conformances: StateMachineHashable, | ||
names: named(hashableIdentifier), named(HashableIdentifier), named(associatedValue)) | ||
public macro StateMachineHashable() = #externalMacro(module: "StateMachineMacros", | ||
type: "StateMachineHashableMacro") |
90 changes: 90 additions & 0 deletions
90
Swift/Sources/StateMachineMacros/Macros/StateMachineHashableMacro.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
// | ||
// Copyright (c) 2019, Match Group, LLC | ||
// BSD License, see LICENSE file for details | ||
// | ||
|
||
import SwiftSyntax | ||
import SwiftSyntaxBuilder | ||
import SwiftSyntaxMacros | ||
|
||
public struct StateMachineHashableMacro: ExtensionMacro { | ||
|
||
public static func expansion( | ||
of node: AttributeSyntax, | ||
attachedTo declaration: some DeclGroupSyntax, | ||
providingExtensionsOf type: some TypeSyntaxProtocol, | ||
conformingTo protocols: [TypeSyntax], | ||
in context: some MacroExpansionContext | ||
) throws -> [ExtensionDeclSyntax] { | ||
|
||
guard let enumDecl: EnumDeclSyntax = .init(declaration) | ||
else { throw StateMachineHashableMacroError.typeMustBeEnumeration } | ||
|
||
let elements: [EnumCaseElementSyntax] = enumDecl | ||
.memberBlock | ||
.members | ||
.compactMap(MemberBlockItemSyntax.init) | ||
.map(\.decl) | ||
.compactMap(EnumCaseDeclSyntax.init) | ||
.flatMap(\.elements) | ||
|
||
guard !elements.isEmpty | ||
else { throw StateMachineHashableMacroError.enumerationMustHaveCases } | ||
|
||
let enumCases: [String] = elements | ||
.map(\.name.text) | ||
.map { "case \($0)" } | ||
|
||
let hashableIdentifierCases: [String] = elements | ||
.map(\.name.text) | ||
.map { "case .\($0):\nreturn .\($0)" } | ||
|
||
let associatedValueCases: [String] = elements.map { element in | ||
if let parameters: EnumCaseParameterListSyntax = element.parameterClause?.parameters, !parameters.isEmpty { | ||
if parameters.count > 1 { | ||
let associatedValues: String = (1...parameters.count) | ||
.map { "value\($0)" } | ||
.joined(separator: ", ") | ||
return """ | ||
case let .\(element.name.text)(\(associatedValues)): | ||
return (\(associatedValues)) | ||
""" | ||
} else { | ||
return """ | ||
case let .\(element.name.text)(value): | ||
return (value) | ||
""" | ||
} | ||
} else { | ||
return """ | ||
case .\(element.name.text): | ||
return () | ||
""" | ||
} | ||
} | ||
|
||
let node: SyntaxNodeString = """ | ||
extension \(type): StateMachineHashable { | ||
enum HashableIdentifier { | ||
\(raw: enumCases.joined(separator: "\n")) | ||
} | ||
var hashableIdentifier: HashableIdentifier { | ||
switch self { | ||
\(raw: hashableIdentifierCases.joined(separator: "\n")) | ||
} | ||
} | ||
var associatedValue: Any { | ||
switch self { | ||
\(raw: associatedValueCases.joined(separator: "\n")) | ||
} | ||
} | ||
} | ||
""" | ||
|
||
return try [ExtensionDeclSyntax(node)] | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
Swift/Sources/StateMachineMacros/Macros/StateMachineHashableMacroError.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// | ||
// Copyright (c) 2019, Match Group, LLC | ||
// BSD License, see LICENSE file for details | ||
// | ||
|
||
public enum StateMachineHashableMacroError: Error, CustomStringConvertible { | ||
|
||
case typeMustBeEnumeration | ||
case enumerationMustHaveCases | ||
|
||
public var description: String { | ||
switch self { | ||
case .typeMustBeEnumeration: | ||
return "Type Must Be Enumeration" | ||
case .enumerationMustHaveCases: | ||
return "Enumeration Must Have Cases" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// | ||
// Copyright (c) 2019, Match Group, LLC | ||
// BSD License, see LICENSE file for details | ||
// | ||
|
||
#if canImport(SwiftCompilerPlugin) | ||
|
||
import SwiftCompilerPlugin | ||
import SwiftSyntaxMacros | ||
|
||
@main | ||
internal struct StateMachineMacros: CompilerPlugin { | ||
|
||
internal let providingMacros: [Macro.Type] = [StateMachineHashableMacro.self] | ||
} | ||
|
||
#endif |
Oops, something went wrong.