-
-
Save aaronpearce/8421f743c28c5349c7d5abfbb7fa1b11 to your computer and use it in GitHub Desktop.
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
import SwiftCompilerPlugin | |
import SwiftSyntax | |
import SwiftSyntaxBuilder | |
import SwiftSyntaxMacros | |
public struct Columns: MemberMacro { | |
public static func expansion( | |
of node: AttributeSyntax, | |
providingMembersOf declaration: some DeclGroupSyntax, | |
in context: some MacroExpansionContext | |
) throws -> [DeclSyntax] { | |
let memberList = declaration.storedProperties() | |
let variables = memberList.compactMap({ property -> String? in | |
// is a property | |
guard | |
let propertyName = property.bindings.first?.pattern.as(IdentifierPatternSyntax.self)?.identifier.text else { | |
return nil | |
} | |
// if it has a Transient macro on it, we can skip it | |
if property.attributes?.first(where: { element in | |
return element.as(AttributeSyntax.self)?.attributeName.as(SimpleTypeIdentifierSyntax.self)?.name.text == "Transient" | |
}) != nil { | |
return nil | |
} else { | |
return "static let \(propertyName) = Column(CodingKeys.\(propertyName))" | |
} | |
}) | |
let columns: DeclSyntax = """ | |
enum Columns { | |
\(raw: variables.joined(separator: "\n")) | |
} | |
""" | |
return [ | |
columns | |
] | |
} | |
} | |
public struct Transient: MemberMacro { | |
public static func expansion( | |
of node: AttributeSyntax, | |
providingMembersOf declaration: some DeclGroupSyntax, | |
in context: some MacroExpansionContext | |
) throws -> [DeclSyntax] { | |
// Does nothing, used only to decorate members with data | |
return [] | |
} | |
} | |
extension VariableDeclSyntax { | |
/// Determine whether this variable has the syntax of a stored property. | |
/// | |
/// This syntactic check cannot account for semantic adjustments due to, | |
/// e.g., accessor macros or property wrappers. | |
var isStoredProperty: Bool { | |
if bindings.count != 1 { | |
return false | |
} | |
let binding = bindings.first! | |
switch binding.accessor { | |
case .none: | |
return true | |
case .accessors(let node): | |
for accessor in node.accessors { | |
switch accessor.accessorKind.tokenKind { | |
case .keyword(.willSet), .keyword(.didSet): | |
// Observers can occur on a stored property. | |
break | |
default: | |
// Other accessors make it a computed property. | |
return false | |
} | |
} | |
return true | |
case .getter: | |
return false | |
@unknown default: | |
return false | |
} | |
} | |
} | |
extension DeclGroupSyntax { | |
/// Enumerate the stored properties that syntactically occur in this | |
/// declaration. | |
func storedProperties() -> [VariableDeclSyntax] { | |
return memberBlock.members.compactMap { member in | |
guard let variable = member.decl.as(VariableDeclSyntax.self), | |
variable.isStoredProperty else { | |
return nil | |
} | |
return variable | |
} | |
} | |
} |
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
@Columns | |
struct Player: Identifiable, Equatable, Codable { | |
var id: Int64? | |
var name: String | |
var score: Int | |
@Transient var temp: Int | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment