-
-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathMetaEnumMacro.swift
More file actions
96 lines (81 loc) · 2.63 KB
/
MetaEnumMacro.swift
File metadata and controls
96 lines (81 loc) · 2.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import MacroToolkit
import SwiftDiagnostics
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros
public struct MetaEnumMacro {
let parentTypeName: String
let metaCases: [EnumCase]
let access: String
let parentParamName: TokenSyntax
init(
node: AttributeSyntax, declaration: some DeclGroupSyntax,
context: some MacroExpansionContext
) throws {
guard let enumDecl = Enum(declaration) else {
throw DiagnosticsError(diagnostics: [
CaseMacroDiagnostic.notAnEnum(declaration).diagnose(at: Syntax(node))
])
}
parentTypeName = enumDecl.identifier
access = enumDecl.isPublic ? "public " : ""
metaCases = enumDecl.cases.map { case_ in
case_.withoutValue()
}
parentParamName = context.makeUniqueName("parent")
}
func makeMetaEnum() -> DeclSyntax {
let caseDecls = metaCases.map { childCase in
"case \(childCase.identifier)"
}.joined(separator: "\n")
return
"""
\(raw: access)enum Meta {
\(raw: caseDecls)
\(makeMetaInit())
}
"""
}
func makeMetaInit() -> DeclSyntax {
let caseStatements = metaCases.map { metaCase in
"""
case .\(metaCase.identifier):
self = .\(metaCase.identifier)
"""
}.joined(separator: "\n")
return
"""
\(raw: access)init(_ \(parentParamName): \(raw: parentTypeName)) {
switch \(parentParamName) {
\(raw: caseStatements)
}
}
"""
}
}
extension MetaEnumMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
let macro = try MetaEnumMacro(node: node, declaration: declaration, context: context)
return [macro.makeMetaEnum()]
}
}
enum CaseMacroDiagnostic {
case notAnEnum(DeclGroupSyntax)
var message: String {
switch self {
case .notAnEnum(let decl):
return
"'@MetaEnum' can only be attached to an enum, not \(decl.textualDeclKind(withArticle: true))"
}
}
func diagnose(at node: Syntax) -> Diagnostic {
DiagnosticBuilder(for: node)
.message(message)
.messageID(MessageID(domain: "MetaEnum", id: Mirror(reflecting: self).children.first?.label ?? "\(self)"))
.build()
}
}