提问人:bubu 提问时间:10/17/2023 最后编辑:Ken Whitebubu 更新时间:10/17/2023 访问量:105
如何将新事例添加到枚举中?
How do I add a new case to an enum?
问:
我正在尝试使用宏和 init 将大小写添加到枚举中,以将其默认为添加的大小写。例如,假设我的枚举是:
@AddEnumCase
enum Employee: String {
case manager
}
那么扩展版本应该给出:
enum Employee: String {
case manager
case developer
init(rawValue: String) {
switch rawValue {
case Self.manager.rawValue:
self = .manager
default:
self = .developer
}
这是我到目前为止的代码。当在 main.swift 上键入任何内容并且测试也成功时,它会成功构建。但是,当在 main.swift 中使用宏时,它无法使用:
命令 SwiftCompile 失败,退出代码为非零
public struct AddEnumCase: MemberMacro {
public static func expansion(of node: ....) throws -> [SwiftSyntax.DeclSyntax] {
guard let enumDecl = declaration.as(EnumDeclSyntax.self) else { return [] }
guard let inheritanceType = enumDecl.inheritanceClause?.inheritedTypes.first?.type else { return [] }
let cases = enumDecl.memberBlock.members.compactMap { $0.decl.as(EnumCaseDeclSyntax.self)?.elements) }
let newElement = try EnumCaseDeclSyntax("case developer")
let initializer = try InitializerDeclSyntax("init(rawValue: \(inheritanceType.trimmed))") {
try SwitchExprSyntax("switch rawValue") {
for name in cases {
SwitchCaseSyntax("""
case Self.\(raw: name).rawValue:
self = .\(raw: name)
""")
}
SwitchCaseSyntax("""
default:
self = .developer
""")
}
}
return [DeclSyntax(newElement), DeclSyntax(initializer)]
}
}
@attached(member, names: arbitrary)
public macro AddEnumCase() = #externalMacro(module: "MyMacros", type: "AddEnumCase")
以下代码在 main.swift 上失败
@AddEnumCase
enum Employee: String {
case Manager
}
答:
1赞
Sweeper
10/17/2023
#1
查看崩溃日志,我可以看到在生成一致性时出了点问题。生成的 in 可能并不详尽,因为它没有考虑您的新案例。rawValue
RawRepresentable
switch
rawValue
快速的宏观扩展不应依赖于扩展的顺序。所有宏都被赋予原始 AST,并且它们“同时”扩展。我认为这也可能适用于如何降低。在宏扩展后,您依赖于降低,这显然不是这种情况。: String
: String
这基本上意味着您还必须生成实现。rawValue
下面是一个示例,仅适用于原始值:String
public struct AddEnumCase: MemberMacro {
public static func expansion(of node: AttributeSyntax, providingMembersOf declaration: some DeclGroupSyntax, in context: some MacroExpansionContext) throws -> [DeclSyntax] {
guard let enumDecl = declaration.as(EnumDeclSyntax.self) else { return [] }
guard let inheritanceType = enumDecl.inheritanceClause?.inheritedTypes.first?.type else { return [] }
let cases = enumDecl.memberBlock.members.compactMap { $0.decl.as(EnumCaseDeclSyntax.self)?.elements }
let newElement = try EnumCaseDeclSyntax("case developer")
let initializer = try InitializerDeclSyntax("init(rawValue: \(String.self))") {
try SwitchExprSyntax("switch rawValue") {
for caseList in cases {
for `case` in caseList {
SwitchCaseSyntax("""
case Self.\(raw: `case`.name).rawValue:
self = .\(raw: `case`.name)
""")
}
}
SwitchCaseSyntax("""
default:
self = .developer
""")
}
}
let rawValue = try VariableDeclSyntax("var rawValue: \(String.self)") {
try SwitchExprSyntax("switch self") {
for caseList in cases {
for `case` in caseList {
if let rawValueExpr = `case`.rawValue?.value {
SwitchCaseSyntax("""
case .\(raw: `case`.name):
return \(rawValueExpr)
""")
} else {
SwitchCaseSyntax("""
case .\(raw: `case`.name):
return \(literal: `case`.name.text)
""")
}
}
}
}
}
return [DeclSyntax(newElement), DeclSyntax(initializer), DeclSyntax(rawValue)]
}
}
对于数值原始值,您需要保留一个针对每种情况递增的计数器,这有点复杂。要 100% 复制 Swift 会生成的内容,您还需要将计数器设置为任何显式声明的原始值。这是留给读者的练习。(:D)
评论
0赞
bubu
10/17/2023
谢谢@Sweeper,这很有帮助!编译器抱怨我的猜测是,这种情况是在 rawValue 语法扩展后添加的,就像您在答案中提到的那样,所以我只是删除了 else 部分并将 rawValue 硬编码为 unknown 与 init 相同。非常感谢!Switch must be exhaustive
评论