|
|
@@ -0,0 +1,120 @@
|
|
|
+// The Swift Programming Language
|
|
|
+// https://docs.swift.org/swift-book
|
|
|
+import SwiftSyntax
|
|
|
+import SwiftSyntaxBuilder
|
|
|
+import SwiftSyntaxMacros
|
|
|
+import SwiftDiagnostics
|
|
|
+import Foundation
|
|
|
+
|
|
|
+// 宏实现:自动生成 Codable 协议的实现
|
|
|
+public struct AutoCodableMacro: MemberMacro {
|
|
|
+ public static func expansion(
|
|
|
+ of node: AttributeSyntax,
|
|
|
+ providingMembersOf declaration: some DeclGroupSyntax,
|
|
|
+ in context: some MacroExpansionContext
|
|
|
+ ) throws -> [DeclSyntax] {
|
|
|
+ // 仅支持类和结构体
|
|
|
+ guard let typeDecl: DeclGroupSyntax = declaration.as(ClassDeclSyntax.self) ?? declaration.as(StructDeclSyntax.self) else {
|
|
|
+ context.diagnose(
|
|
|
+ Diagnostic(
|
|
|
+ node: node,
|
|
|
+ message: AutoCodableMacroError.onlyClassesAndStructs
|
|
|
+ )
|
|
|
+ )
|
|
|
+ return []
|
|
|
+ }
|
|
|
+
|
|
|
+ // 提取所有存储属性
|
|
|
+ let properties: [(name: String, type: String)] = typeDecl.memberBlock.members.compactMap { member -> (name: String, type: String)? in
|
|
|
+ // 1. 检查是否是变量声明
|
|
|
+ guard let variableDecl = member.decl.as(VariableDeclSyntax.self) else {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 确保只处理单个属性的声明
|
|
|
+ guard variableDecl.bindings.count == 1,
|
|
|
+ let binding = variableDecl.bindings.first else {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 提取属性名称
|
|
|
+ guard let identifierPattern = binding.pattern.as(IdentifierPatternSyntax.self) else {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ let name = identifierPattern.identifier.text
|
|
|
+
|
|
|
+ // 4. 提取属性类型
|
|
|
+ guard let typeAnnotation = binding.typeAnnotation else {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ let type = typeAnnotation.type.description.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
+
|
|
|
+ return (name: name, type: type)
|
|
|
+ }
|
|
|
+
|
|
|
+ if properties.isEmpty {
|
|
|
+ context.diagnose(
|
|
|
+ Diagnostic(
|
|
|
+ node: node,
|
|
|
+ message: AutoCodableMacroError.noStoredProperties
|
|
|
+ )
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成 CodingKeys 枚举
|
|
|
+ let codingKeys = try EnumDeclSyntax("enum CodingKeys: String, CodingKey") {
|
|
|
+ for property in properties {
|
|
|
+ DeclSyntax("case \(raw: property.name)")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成 init(from:) 方法
|
|
|
+ let bodyBuilder: SyntaxNodeString = declaration is ClassDeclSyntax ? "required init(from decoder: Decoder) throws" : "init(from decoder: Decoder) throws"
|
|
|
+ let initFrom = try InitializerDeclSyntax(bodyBuilder) {
|
|
|
+ CodeBlockItemListSyntax {
|
|
|
+ "let container = try decoder.container(keyedBy: CodingKeys.self)"
|
|
|
+ for property in properties {
|
|
|
+ """
|
|
|
+ if let value = try? container.decode(\(raw: property.type).self, forKey: .\(raw: property.name)) {
|
|
|
+ self.\(raw: property.name) = value
|
|
|
+ }
|
|
|
+ """
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成 encode(to:) 方法
|
|
|
+ let encodeTo = try FunctionDeclSyntax("func encode(to encoder: Encoder) throws") {
|
|
|
+ CodeBlockItemListSyntax {
|
|
|
+ "var container = encoder.container(keyedBy: CodingKeys.self)"
|
|
|
+ for property in properties {
|
|
|
+ "try container.encode(\(raw: property.name), forKey: .\(raw: property.name))"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return [
|
|
|
+ DeclSyntax(codingKeys),
|
|
|
+ DeclSyntax(initFrom),
|
|
|
+ DeclSyntax(encodeTo)
|
|
|
+ ]
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 错误提示定义
|
|
|
+enum AutoCodableMacroError: DiagnosticMessage {
|
|
|
+ case onlyClassesAndStructs
|
|
|
+ case noStoredProperties
|
|
|
+
|
|
|
+ var message: String {
|
|
|
+ switch self {
|
|
|
+ case .onlyClassesAndStructs:
|
|
|
+ "AutoCodable can only be applied to classes and structs"
|
|
|
+ case .noStoredProperties:
|
|
|
+ "Type has no stored properties; Codable implementation will be empty"
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var diagnosticID: MessageID { .init(domain: "Codable", id: "\(self)") }
|
|
|
+ var severity: DiagnosticSeverity { .error }
|
|
|
+}
|