|
7 | 7 |
|
8 | 8 | import Foundation
|
9 | 9 | import Parchment
|
| 10 | +import SwiftSyntax |
| 11 | +import SwiftSyntaxBuilder |
| 12 | + |
| 13 | +struct GeneratedEvent: Loggable { |
| 14 | + let eventName: String |
| 15 | + let parameters: [String : Sendable] |
| 16 | + |
| 17 | + static func token<T>(_ keyPath: KeyPath<Self, T>) -> TokenSyntax { |
| 18 | + switch keyPath { |
| 19 | + case \.eventName: |
| 20 | + return .identifier("eventName") |
| 21 | + case \.parameters: |
| 22 | + return .identifier("parameters") |
| 23 | + default: |
| 24 | + fatalError() |
| 25 | + } |
| 26 | + } |
| 27 | + |
| 28 | + static func identifierPattern<T>( |
| 29 | + _ keyPath: KeyPath<Self, T> |
| 30 | + ) -> IdentifierPatternSyntax { |
| 31 | + switch keyPath { |
| 32 | + case \.eventName: |
| 33 | + return "eventName" |
| 34 | + case \.parameters: |
| 35 | + return "parameters" |
| 36 | + default: |
| 37 | + fatalError() |
| 38 | + } |
| 39 | + } |
| 40 | +} |
10 | 41 |
|
11 | 42 | public struct SwiftGenerator {
|
12 | 43 | enum Error: Swift.Error {
|
13 | 44 | case argumentsIsEmpty
|
14 | 45 | }
|
15 | 46 |
|
16 |
| - public init() {} |
| 47 | + public init() { |
| 48 | + |
| 49 | + } |
17 | 50 |
|
18 | 51 | public func run(with definisions: [EventDefinision]) throws -> String {
|
19 | 52 | guard !definisions.isEmpty else { throw Error.argumentsIsEmpty }
|
20 | 53 | return generate(with: definisions)
|
21 | 54 | }
|
22 | 55 |
|
23 | 56 | private func generate(with definisions: [EventDefinision]) -> String {
|
24 |
| - """ |
25 |
| - // This file is automatically generated by eventgen. |
26 |
| - // Do not edit this file. |
| 57 | + SourceFileSyntax { |
| 58 | + ImportDeclSyntax(path: [.init(name: "Parchment")]) |
| 59 | + generatedEventStructDecl |
| 60 | + extensionDecl(with: definisions) |
| 61 | + }.formatted().description |
| 62 | + } |
27 | 63 |
|
28 |
| - struct GeneratedEvent: \(Loggable.self) { |
29 |
| - let eventName: String |
30 |
| - let paramerters: [String: Sendasble] |
| 64 | + private var generatedEventStructDecl: StructDeclSyntax { |
| 65 | + StructDeclSyntax( |
| 66 | + identifier: "\(GeneratedEvent.self)", |
| 67 | + inheritanceClause: TypeInheritanceClauseSyntax { |
| 68 | + InheritedTypeSyntax( |
| 69 | + typeName: SimpleTypeIdentifierSyntax( |
| 70 | + stringLiteral: "\(Loggable.self)" |
| 71 | + ) |
| 72 | + ) |
| 73 | + } |
| 74 | + ) { |
| 75 | + VariableDeclSyntax( |
| 76 | + .let, |
| 77 | + name: GeneratedEvent.identifierPattern(\.eventName), |
| 78 | + type: TypeAnnotationSyntax( |
| 79 | + type: SimpleTypeIdentifierSyntax(stringLiteral: "\(String.self)") |
| 80 | + ) |
| 81 | + ) |
| 82 | + VariableDeclSyntax( |
| 83 | + .let, |
| 84 | + name: GeneratedEvent.identifierPattern(\.parameters), |
| 85 | + type: TypeAnnotationSyntax( |
| 86 | + type: DictionaryTypeSyntax( |
| 87 | + keyType: SimpleTypeIdentifierSyntax( |
| 88 | + stringLiteral: "\(String.self)" |
| 89 | + ), |
| 90 | + valueType: SimpleTypeIdentifierSyntax( |
| 91 | + stringLiteral: "Sendable" |
| 92 | + ) |
| 93 | + ) |
| 94 | + ) |
| 95 | + ) |
31 | 96 | }
|
| 97 | + } |
32 | 98 |
|
33 |
| - extension GeneratedEvent { |
34 |
| - \(indented: generateFunc(with: definisions.filter { !$0.properties.isEmpty })) |
35 |
| - \(indented: generateProperty(with: definisions.filter { $0.properties.isEmpty })) |
| 99 | + private func extensionDecl(with definisions: [EventDefinision]) -> ExtensionDeclSyntax { |
| 100 | + ExtensionDeclSyntax( |
| 101 | + extendedType: SimpleTypeIdentifierSyntax( |
| 102 | + stringLiteral: "\(GeneratedEvent.self)" |
| 103 | + ) |
| 104 | + ) { |
| 105 | + for definision in definisions { |
| 106 | + if definision.properties.isEmpty { |
| 107 | + propertyEventDecl(with: definision) |
| 108 | + } else { |
| 109 | + functionEventDecl(with: definision) |
| 110 | + } |
| 111 | + |
| 112 | + } |
36 | 113 | }
|
37 |
| - """ |
38 | 114 | }
|
39 | 115 |
|
40 |
| - private func generateFunc(with definision: EventDefinision) -> String { |
41 |
| - """ |
42 |
| - \(generateCodeDocument(with: definision)) |
43 |
| - static func \(definision.name)\(generateTupple(with: definision.properties)) -> Self { |
44 |
| - .init( |
45 |
| - eventName: "\(definision.name)", |
46 |
| - parameters: \(generateParamertersDictonary(with: definision)) |
| 116 | + private func propertyEventDecl(with definision: EventDefinision) -> VariableDeclSyntax { |
| 117 | + VariableDeclSyntax( |
| 118 | + leadingTrivia: .init( |
| 119 | + pieces: generateCodeDocument(with: definision) |
| 120 | + ), |
| 121 | + modifiers: ModifierListSyntax { |
| 122 | + DeclModifierSyntax(name: .static) |
| 123 | + }, |
| 124 | + name: .init(stringLiteral: definision.name), |
| 125 | + type: TypeAnnotationSyntax( |
| 126 | + type: SimpleTypeIdentifier(stringLiteral: "Self") |
47 | 127 | )
|
| 128 | + ) { |
| 129 | + generatedEventInitialization(with: definision) |
48 | 130 | }
|
49 |
| - """ |
50 | 131 | }
|
51 | 132 |
|
52 |
| - private func generateProperty(with definision: EventDefinision) -> String { |
53 |
| - """ |
54 |
| - \(generateCodeDocument(with: definision)) |
55 |
| - static var \(definision.name): Self { |
56 |
| - .init(eventName: \(definision.name), parameters: [:]) |
| 133 | + private func functionEventDecl( |
| 134 | + with definision: EventDefinision |
| 135 | + ) -> FunctionDeclSyntax { |
| 136 | + FunctionDeclSyntax( |
| 137 | + leadingTrivia: .init( |
| 138 | + pieces: generateCodeDocument(with: definision) |
| 139 | + ), |
| 140 | + modifiers: ModifierListSyntax { |
| 141 | + DeclModifierSyntax(name: .static) |
| 142 | + }, |
| 143 | + identifier: .identifier(definision.name), |
| 144 | + signature: FunctionSignatureSyntax( |
| 145 | + input: ParameterClauseSyntax { |
| 146 | + for property in definision.properties { |
| 147 | + FunctionParameterSyntax( |
| 148 | + firstName: .identifier(property.name), |
| 149 | + colon: .colon, |
| 150 | + type: typeSyntax( |
| 151 | + property.type, isNullable: property.nullable |
| 152 | + ) |
| 153 | + ) |
| 154 | + } |
| 155 | + }, |
| 156 | + output: ReturnClause( |
| 157 | + returnType: SimpleTypeIdentifierSyntax( |
| 158 | + stringLiteral: "Self" |
| 159 | + ) |
| 160 | + ) |
| 161 | + ) |
| 162 | + ) { |
| 163 | + functionEventBodyDecl(with: definision) |
57 | 164 | }
|
58 |
| - """ |
59 | 165 | }
|
60 | 166 |
|
61 |
| - private func generateFunc(with definisions: [EventDefinision]) -> String { |
62 |
| - definisions.map(generateFunc(with:)).joined(separator: "\n") |
| 167 | + private func functionEventBodyDecl(with definision: EventDefinision) -> CodeBlockItemListSyntax { |
| 168 | + CodeBlockItemListSyntax { |
| 169 | + generatedEventInitialization(with: definision) |
| 170 | + } |
63 | 171 | }
|
64 | 172 |
|
65 |
| - private func generateProperty(with definisions: [EventDefinision]) -> String { |
66 |
| - definisions.map(generateProperty(with:)).joined(separator: "\n") |
| 173 | + private func generatedEventInitialization( |
| 174 | + with definision: EventDefinision |
| 175 | + ) -> FunctionCallExprSyntax { |
| 176 | + FunctionCallExprSyntax( |
| 177 | + callee: IdentifierExprSyntax(stringLiteral: "\(GeneratedEvent.self)") |
| 178 | + ) { |
| 179 | + TupleExprElementSyntax( |
| 180 | + label: GeneratedEvent.token(\.eventName), |
| 181 | + colon: .colon, |
| 182 | + expression: StringLiteralExprSyntax( |
| 183 | + content: definision.name |
| 184 | + ) |
| 185 | + ) |
| 186 | + TupleExprElementSyntax( |
| 187 | + label: GeneratedEvent.token(\.parameters), |
| 188 | + colon: .colon, |
| 189 | + expression: DictionaryExprSyntax { |
| 190 | + for property in definision.properties { |
| 191 | + DictionaryElementSyntax.init( |
| 192 | + keyExpression: StringLiteralExprSyntax( |
| 193 | + content: property.name |
| 194 | + ), |
| 195 | + valueExpression: IdentifierExpr(stringLiteral: property.name) |
| 196 | + ) |
| 197 | + } |
| 198 | + } |
| 199 | + ) |
| 200 | + } |
67 | 201 | }
|
68 | 202 |
|
69 |
| - private func generateParamertersDictonary(with definision: EventDefinision) -> String { |
70 |
| - "[\(definision.properties.map { "\($0.name): \($0.name)" }.joined(separator: ", "))]" |
| 203 | + private func generateCodeDocument(with definision: EventDefinision) -> [TriviaPiece] { |
| 204 | + if !definision.properties.isEmpty { |
| 205 | + var result: [TriviaPiece] = [ |
| 206 | + .docLineComment("/// \(definision.description)"), |
| 207 | + .newlines(1), |
| 208 | + .docLineComment("/// - Parameters:"), |
| 209 | + .newlines(1) |
| 210 | + ] |
| 211 | + |
| 212 | + for property in definision.properties { |
| 213 | + result.append( |
| 214 | + .docLineComment("/// - \(property.name): \(property.description)") |
| 215 | + ) |
| 216 | + result.append(.newlines(1)) |
| 217 | + } |
| 218 | + |
| 219 | + return result |
| 220 | + } else { |
| 221 | + return [ |
| 222 | + .docLineComment("/// \(definision.description)"), |
| 223 | + .newlines(1) |
| 224 | + ] |
| 225 | + } |
71 | 226 | }
|
72 | 227 |
|
73 |
| - private func generateTupple(with fields: [Field]) -> String { |
| 228 | + private func typeSyntax(_ typeString: String, isNullable: Bool) -> TypeSyntaxProtocol { |
74 | 229 | func type(_ typeString: String, isNullable: Bool) -> String {
|
75 |
| - var result: String |
76 | 230 | switch typeString {
|
77 | 231 | case "string":
|
78 |
| - result = "\(String.self)" |
| 232 | + return "\(String.self)" |
79 | 233 | case "int":
|
80 |
| - result = "\(Int.self)" |
| 234 | + return "\(Int.self)" |
81 | 235 | case "double":
|
82 |
| - result = "\(Double.self)" |
| 236 | + return "\(Double.self)" |
83 | 237 | case "boolean":
|
84 |
| - result = "\(Bool.self)" |
| 238 | + return "\(Bool.self)" |
85 | 239 | default:
|
86 | 240 | fatalError("Detect unsupported type \(typeString)")
|
87 | 241 | }
|
88 |
| - |
89 |
| - if isNullable { |
90 |
| - result += "?" |
91 |
| - } |
92 |
| - |
93 |
| - return result |
94 | 242 | }
|
95 |
| - return "(" + fields.map { "\($0.name): \(type($0.type, isNullable: $0.nullable))" }.joined(separator: ", ") + ")" |
96 |
| - } |
97 | 243 |
|
98 |
| - private func generateCodeDocument(with definision: EventDefinision) -> String { |
99 |
| - if !definision.properties.isEmpty { |
100 |
| - return """ |
101 |
| - /// \(definision.description) |
102 |
| - /// - Parameters: |
103 |
| - \(definision.properties.map { "/// - \($0.name): \($0.description)" }.joined(separator: "\n")) |
104 |
| - """ |
| 244 | + let simpleSyntax = SimpleTypeIdentifierSyntax( |
| 245 | + stringLiteral: type( |
| 246 | + typeString, isNullable: isNullable |
| 247 | + ) |
| 248 | + ) |
| 249 | + if isNullable { |
| 250 | + return simpleSyntax |
105 | 251 | } else {
|
106 |
| - return """ |
107 |
| - /// \(definision.description) |
108 |
| - """ |
| 252 | + return OptionalTypeSyntax(wrappedType: simpleSyntax) |
109 | 253 | }
|
110 | 254 | }
|
111 | 255 | }
|
|
0 commit comments