diff --git a/SwiftMetaModelGenerator/DeclarationCollector.swift b/SwiftMetaModelGenerator/DeclarationCollector.swift index 94c760f35ccb5c32be58fe94ca7a5ceb45da71b5..b320b112ad5e6a3919b3484764f7d2313c7acaeb 100644 --- a/SwiftMetaModelGenerator/DeclarationCollector.swift +++ b/SwiftMetaModelGenerator/DeclarationCollector.swift @@ -8,20 +8,51 @@ import Foundation import SwiftSyntax -struct ClassDeclaration { +class ClassDeclaration { let name: String - let variables: [String] + let parents: [String] + + let variables: [Variable] + var needsMethodOverride: Bool + + init(name: String, parents: [String], variables: [Variable]) { + self.name = name + self.parents = parents + self.variables = variables + self.needsMethodOverride = false + } +} + +class Variable { + let name: String + let type: String? + let optional: Bool? + let constant: Bool + let privateVisibility: Bool + let staticModifier: Bool + let hasAccessor: Bool + + init(name: String, type: String?, optional: Bool?, constant: Bool, privateVisibility: Bool, staticModifier: Bool, hasAccessor: Bool) { + self.name = name + self.type = type + self.optional = optional + self.constant = constant + self.privateVisibility = privateVisibility + self.staticModifier = staticModifier + self.hasAccessor = hasAccessor + } } class DeclarationCollector: SyntaxVisitor { private(set) var classes: [ClassDeclaration] = [] - private var currentVariables: [String] = [] + private var currentVariables: [Variable] = [] override func visitPost(_ node: ClassDeclSyntax) { let name = node.identifier.text.trimmingCharacters(in: .whitespacesAndNewlines) + let inherences = node.inheritanceClause?.inheritedTypeCollection.map({ $0.typeName.description.trimmingCharacters(in: .whitespacesAndNewlines) }) ?? [] - classes.append(ClassDeclaration(name: name, variables: currentVariables)) + classes.append(ClassDeclaration(name: name, parents: inherences, variables: currentVariables)) currentVariables = [] } @@ -29,7 +60,18 @@ class DeclarationCollector: SyntaxVisitor { for child: PatternBindingSyntax in node.bindings { if let parent = child.context as? VariableDeclSyntax, parent.context is ClassDeclSyntax { let name = child.pattern.description.trimmingCharacters(in: .whitespacesAndNewlines) - currentVariables.append(name) + let type = child.typeAnnotation?.type.description.trimmingCharacters(in: .whitespacesAndNewlines) + let constant = parent.letOrVarKeyword.text.trimmingCharacters(in: .whitespacesAndNewlines) == "let" + + let privateVisibility = parent.modifiers?.map({ $0.name.text }).contains("private") ?? false + let staticModifier = parent.modifiers?.map({ $0.name.text }).contains("static") ?? false + let hasAccessor = child.accessor != nil + + if type == nil { + print("Fail to observe type for \(name)") + } + + currentVariables.append(Variable(name: name, type: type?.replacingOccurrences(of: "?", with: ""), optional: type?.hasSuffix("?") , constant: constant, privateVisibility: privateVisibility, staticModifier: staticModifier, hasAccessor: hasAccessor)) } } return .skipChildren diff --git a/SwiftMetaModelGenerator/main.swift b/SwiftMetaModelGenerator/main.swift index 1b842cd58687868f2c06eceef9e71720f9d2a1d4..70007e72e15eb69d5800c19bcc26a14e67378dfd 100644 --- a/SwiftMetaModelGenerator/main.swift +++ b/SwiftMetaModelGenerator/main.swift @@ -30,6 +30,12 @@ struct SwiftMetaModelGenerator: ParsableCommand } else { classes = try processFile(file: inputPath) } + + for clazz in classes { + if clazz.parents.contains(where: { classes.map(\.name).contains($0) }) { + clazz.needsMethodOverride = true + } + } let context = [ "classes": classes diff --git a/SwiftMetaModelGenerator/templates/meta_model.stencil b/SwiftMetaModelGenerator/templates/meta_model.stencil index 6fd4078b97dfc895a17c50c86848ed25e271f056..347277d16fa2bacc6b5e363dc9612b070ef3eff6 100644 --- a/SwiftMetaModelGenerator/templates/meta_model.stencil +++ b/SwiftMetaModelGenerator/templates/meta_model.stencil @@ -8,10 +8,29 @@ import Foundation // swiftlint:disable attributes file_length vertical_whitespace_closing_braces // swiftlint:disable identifier_name line_length type_body_length {% for class in classes %} -class {{class.name}}_ { +class {{class.name}}_ +{ {% for variable in class.variables %} - static let {{ variable }}: String = "{{ variable }}" + static let {{ variable.name }}: String = "{{ variable.name }}" {% endfor %} } + +extension {{class.name}} +{ + @objc {% if class.needsMethodOverride %} override {% endif %} func setValueByKey(key: String, value: Any?) + { + switch key { + {% for variable in class.variables %} + {% if not variable.constant and not variable.privateVisibility and not variable.staticModifier and not variable.hasAccessor %} + case "{{variable.name}}": + self.{{ variable.name }} = value {% if variable.optional %} as? {% else %} as! {% endif %} {{ variable.type }} + {% endif %} + {% endfor %} + default: + fatalError("Cannot set value for key \(key)") + } + } +} + {% endfor %} // swiftlint:enable identifier_name line_length type_body_length