diff --git a/Plugins/Libraries/LighterCodeGenAST/Generation/GenExtensions.swift b/Plugins/Libraries/LighterCodeGenAST/Generation/GenExtensions.swift index b064b9c..9892201 100644 --- a/Plugins/Libraries/LighterCodeGenAST/Generation/GenExtensions.swift +++ b/Plugins/Libraries/LighterCodeGenAST/Generation/GenExtensions.swift @@ -1,6 +1,6 @@ // // Created by Helge Heß. -// Copyright © 2022 ZeeZide GmbH. +// Copyright © 2022-2024 ZeeZide GmbH. // public extension CodeGenerator { @@ -42,9 +42,9 @@ public extension CodeGenerator { } indent { - for structure in value.structures { + for typeDefinition in value.typeDefinitions { writeln() - generateStruct(structure, omitPublic: value.public) + generateTypeDefinition(typeDefinition, omitPublic: value.public) } for function in value.typeFunctions { diff --git a/Plugins/Libraries/LighterCodeGenAST/Generation/GenStructures.swift b/Plugins/Libraries/LighterCodeGenAST/Generation/GenStructures.swift index f854090..0ffe8f5 100644 --- a/Plugins/Libraries/LighterCodeGenAST/Generation/GenStructures.swift +++ b/Plugins/Libraries/LighterCodeGenAST/Generation/GenStructures.swift @@ -7,7 +7,7 @@ public extension CodeGenerator { /// public static let schema = Schema() /// public var personId : Int - func generateInstanceVariable(_ value : Struct.InstanceVariable, + func generateInstanceVariable(_ value : TypeDefinition.InstanceVariable, `static` : Bool = false, omitPublic : Bool = false) { @@ -77,7 +77,34 @@ public extension CodeGenerator { writePlain() } } - + + /** + * ```swift + * public struct Person: SQLKeyedTableRecord, Identifiable { + * + * public static let schema = Schema() + * + * @inlinable + * public var id : Int { personId } + * + * public var personId : Int + * public var lastname : String + * public var firstname : String? + * + * @inlinable + * public init(personId: Int = 0, lastname: String, firstname: String? = nil) { + * self.personId = personId + * self.lastname = lastname + * self.firstname = firstname + * } + * } + * ``` + */ + func generateStruct(_ value: TypeDefinition, omitPublic: Bool = false) { + assert(value.kind == .struct) + generateTypeDefinition(value, omitPublic: omitPublic) + } + /** * ```swift * public struct Person: SQLKeyedTableRecord, Identifiable { @@ -100,7 +127,8 @@ public extension CodeGenerator { * } * ``` */ - func generateStruct(_ value: Struct, omitPublic: Bool = false) { + func generateTypeDefinition(_ value: TypeDefinition, omitPublic: Bool = false) + { if !source.isEmpty { appendEOLIfMissing() } if let comment = value.comment { @@ -114,7 +142,11 @@ public extension CodeGenerator { do { // header appendIndent() if value.public && !omitPublic { append("public ") } - append("struct ") + switch value.kind { + case .struct : append("struct ") + case .class : append("class ") + case .enum : append("enum ") + } append(tickedWhenReserved(value.name)) if !value.conformances.isEmpty { @@ -129,12 +161,13 @@ public extension CodeGenerator { if !value.typeAliases.isEmpty { writeln() } for ( name, ref ) in value.typeAliases { - writeln("\(pubPrefix)typealias \(tickedWhenReserved(name))\(configuration.propertyValueSeparator)\(string(for: ref))") + writeln("\(pubPrefix)typealias \(tickedWhenReserved(name))" + + "\(configuration.propertyValueSeparator)\(string(for: ref))") } - for structure in value.structures { + for nestedType in value.nestedTypes { writeln() - generateStruct(structure) + generateStruct(nestedType) } // Later: I'd really like to vertically align the colors and equals. diff --git a/Plugins/Libraries/LighterCodeGenAST/Generation/GenUnit.swift b/Plugins/Libraries/LighterCodeGenAST/Generation/GenUnit.swift index 593d94b..67876c8 100644 --- a/Plugins/Libraries/LighterCodeGenAST/Generation/GenUnit.swift +++ b/Plugins/Libraries/LighterCodeGenAST/Generation/GenUnit.swift @@ -1,6 +1,6 @@ // // Created by Helge Heß. -// Copyright © 2022 ZeeZide GmbH. +// Copyright © 2022-2024 ZeeZide GmbH. // public extension CodeGenerator { @@ -18,9 +18,9 @@ public extension CodeGenerator { generateFunctionDefinition(function) } - for structure in unit.structures { + for typeDefinition in unit.typeDefinitions { writeln() - generateStruct(structure) + generateTypeDefinition(typeDefinition) } for ext in unit.extensions { diff --git a/Plugins/Libraries/LighterCodeGenAST/Nodes/CompilationUnit.swift b/Plugins/Libraries/LighterCodeGenAST/Nodes/CompilationUnit.swift index 9a3c83d..a5523d7 100644 --- a/Plugins/Libraries/LighterCodeGenAST/Nodes/CompilationUnit.swift +++ b/Plugins/Libraries/LighterCodeGenAST/Nodes/CompilationUnit.swift @@ -1,6 +1,6 @@ // // Created by Helge Heß. -// Copyright © 2022 ZeeZide GmbH. +// Copyright © 2022-2024 ZeeZide GmbH. // /** @@ -20,25 +20,35 @@ public struct CompilationUnit { public var reexports : [ String ] = [] /// The structures that are part of the unit. - public var structures : [ Struct ] + public var typeDefinitions : [ TypeDefinition ] /// The functions that are part of the unit. - public var functions : [ FunctionDefinition ] + public var functions : [ FunctionDefinition ] /// The extensions that are part of the unit. - public var extensions : [ Extension ] + public var extensions : [ Extension ] /// Initialize a new CompilationUnit, only name and extensions are required. - public init(name : String, - imports : [ String ] = [], - structures : [ Struct ] = [], - functions : [ FunctionDefinition ] = [], - extensions : [ Extension ] = []) + public init(name : String, + imports : [ String ] = [], + typeDefinitions : [ TypeDefinition ] = [], + functions : [ FunctionDefinition ] = [], + extensions : [ Extension ] = []) { self.name = name self.imports = imports - self.structures = structures + self.typeDefinitions = typeDefinitions self.functions = functions self.extensions = extensions } + /// Initialize a new CompilationUnit, only name and extensions are required. + public init(name : String, + imports : [ String ] = [], + structures : [ TypeDefinition ], // legacy compat + functions : [ FunctionDefinition ] = [], + extensions : [ Extension ] = []) + { + self.init(name: name, imports: imports, typeDefinitions: structures, + functions: functions, extensions: extensions) + } } diff --git a/Plugins/Libraries/LighterCodeGenAST/Nodes/Extension.swift b/Plugins/Libraries/LighterCodeGenAST/Nodes/Extension.swift index cf2e6e6..dffbf81 100644 --- a/Plugins/Libraries/LighterCodeGenAST/Nodes/Extension.swift +++ b/Plugins/Libraries/LighterCodeGenAST/Nodes/Extension.swift @@ -21,7 +21,7 @@ public struct Extension { // MARK: - Types /// The structures added to the ``extendedType``. - public var structures : [ Struct ] + public var typeDefinitions : [ TypeDefinition ] // MARK: - Functions @@ -40,7 +40,7 @@ public struct Extension { public init(extendedType : TypeReference, `public` : Bool = true, genericConstraints : [ GenericConstraint ] = [], - structures : [ Struct ] = [], + typeDefinitions : [ TypeDefinition ] = [], typeFunctions : [ FunctionDefinition ] = [], functions : [ FunctionDefinition ] = [], minimumSwiftVersion : ( major: Int, minor: Int )? = nil, @@ -48,7 +48,7 @@ public struct Extension { { self.public = `public` self.extendedType = extendedType - self.structures = structures + self.typeDefinitions = typeDefinitions self.typeFunctions = typeFunctions self.functions = functions self.genericConstraints = genericConstraints @@ -56,8 +56,9 @@ public struct Extension { self.requiredImports = requiredImports } + @inlinable public var isEmpty : Bool { - functions.isEmpty && structures.isEmpty && typeFunctions.isEmpty + functions.isEmpty && typeDefinitions.isEmpty && typeFunctions.isEmpty } } diff --git a/Plugins/Libraries/LighterCodeGenAST/Nodes/Struct.swift b/Plugins/Libraries/LighterCodeGenAST/Nodes/TypeDefinition.swift similarity index 93% rename from Plugins/Libraries/LighterCodeGenAST/Nodes/Struct.swift rename to Plugins/Libraries/LighterCodeGenAST/Nodes/TypeDefinition.swift index 026d3dc..49e7143 100644 --- a/Plugins/Libraries/LighterCodeGenAST/Nodes/Struct.swift +++ b/Plugins/Libraries/LighterCodeGenAST/Nodes/TypeDefinition.swift @@ -6,7 +6,11 @@ /** * An AST node representing a Swift structure type. */ -public struct Struct { +public struct TypeDefinition { + + public enum Kind: Int, Sendable { + case `struct`, `class`, `enum` + } /** * An instance variable of a ``Struct``. @@ -64,13 +68,15 @@ public struct Struct { /// The types the structure conforms to, e.g. `SQLTableRecord`. public var conformances : [ TypeReference ] + public var kind : Kind + // MARK: - Types /// A set of type aliases that should be declare in the structure, /// e.g. `typealias RecordType = Person`. public var typeAliases : [ ( name: String, type: TypeReference ) ] /// A set of structures nested in this structure. - public var structures : [ Struct ] + public var nestedTypes : [ TypeDefinition ] // MARK: - Variables @@ -94,11 +100,12 @@ public struct Struct { /// Intialize a new struct AST node. Only the `name` is required. public init(dynamicMemberLookup : Bool = false, public : Bool = true, + kind : Kind, name : String, conformances : [ TypeReference ] = [], typeAliases : [ ( name: String, type: TypeReference ) ] = [], - structures : [ Struct ] = [], + nestedTypes : [ TypeDefinition ] = [], typeVariables : [ InstanceVariable ] = [], variables : [ InstanceVariable ] = [], computedTypeProperties : [ ComputedPropertyDefinition ] = [], @@ -109,10 +116,11 @@ public struct Struct { { self.dynamicMemberLookup = dynamicMemberLookup self.public = `public` + self.kind = kind self.name = name self.conformances = conformances self.typeAliases = typeAliases - self.structures = structures + self.nestedTypes = nestedTypes self.typeVariables = typeVariables self.variables = variables self.computedTypeProperties = computedTypeProperties @@ -130,7 +138,7 @@ public struct Struct { // MARK: - Convenience -public extension Struct.InstanceVariable { +public extension TypeDefinition.InstanceVariable { /// Initialize a new instance variable node for a `let`. static func `let`(public: Bool = true, _ name: String, @@ -176,5 +184,5 @@ public extension Struct.InstanceVariable { } #if swift(>=5.5) -extension Struct : Sendable {} +extension TypeDefinition : Sendable {} #endif diff --git a/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateCombinedFile.swift b/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateCombinedFile.swift index 847cf83..e553224 100644 --- a/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateCombinedFile.swift +++ b/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateCombinedFile.swift @@ -51,14 +51,14 @@ extension EnlighterASTGenerator { // Database Structure - unit.structures.append( + unit.typeDefinitions.append( generateDatabaseStructure(moduleFileName: moduleFileName)) // Entity Structures if !options.nestRecordTypesInDatabase { - unit.structures += database.entities.map { + unit.typeDefinitions += database.entities.map { generateRecordStructure(for: $0) } } @@ -78,7 +78,7 @@ extension EnlighterASTGenerator { unit.extensions += database.entities.map { entity in Extension( extendedType: globalTypeRef(of: entity), - structures: [ generateSchemaStructure(for: entity) ], + typeDefinitions: [ generateSchemaStructure(for: entity) ], functions: [ generateRecordStatementInit(for: entity), generateRecordStatementBind(for: entity) diff --git a/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateDatabaseStruct.swift b/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateDatabaseStruct.swift index 7d70c76..23bb7b9 100644 --- a/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateDatabaseStruct.swift +++ b/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateDatabaseStruct.swift @@ -8,15 +8,15 @@ import LighterCodeGenAST extension EnlighterASTGenerator { public func generateDatabaseStructure(moduleFileName: String? = nil) - -> Struct + -> TypeDefinition { let typeAliases = calculateClassTypeAliases() - var structures = [ Struct ]() - var typeVariables = [ Struct.InstanceVariable ]() - var variables = [ Struct.InstanceVariable ]() - var computedTypeProperties = [ ComputedPropertyDefinition ]() - var typeFunctions = [ FunctionDefinition ]() - var functions = [ FunctionDefinition ]() + var structures = [ TypeDefinition ]() + var typeVariables = [ TypeDefinition.InstanceVariable ]() + var variables = [ TypeDefinition.InstanceVariable ]() + var computedTypeProperties = [ ComputedPropertyDefinition ]() + var typeFunctions = [ FunctionDefinition ]() + var functions = [ FunctionDefinition ]() if options.useLighter, let filename = moduleFileName, options.allowFoundation // needs `Bundle` @@ -161,13 +161,14 @@ extension EnlighterASTGenerator { // Assemble the structure - return Struct( + return TypeDefinition( dynamicMemberLookup : options.useLighter, public : options.public, + kind : .struct, name : database.name, conformances : dbTypeConformances, typeAliases : typeAliases, - structures : structures, + nestedTypes : structures, typeVariables : typeVariables, variables : variables, computedTypeProperties : computedTypeProperties, @@ -554,11 +555,12 @@ extension EnlighterASTGenerator { public let addresses = Address.self } */ - fileprivate func generateRecordTypesStruct(useAlias suffix: String?) -> Struct + fileprivate func generateRecordTypesStruct(useAlias suffix: String?) + -> TypeDefinition { let firstEntity = database.entities.first ?? .init(name: "NoTypes") - return Struct( - public: options.public, + return TypeDefinition( + public: options.public, kind: .struct, name: api.recordTypeLookupTarget, conformances: [ .name("Swift.Sendable") ], variables: database.entities.map { diff --git a/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateDatabaseSupport.swift b/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateDatabaseSupport.swift index 4860b5a..75e8753 100644 --- a/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateDatabaseSupport.swift +++ b/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateDatabaseSupport.swift @@ -143,7 +143,8 @@ extension EnlighterASTGenerator { * ``` */ func generateModuleSingleton(name propertyName: String = "module", - for filename: String) -> Struct.InstanceVariable + for filename: String) + -> TypeDefinition.InstanceVariable { let name : Expression let ext : Expression @@ -191,9 +192,9 @@ extension EnlighterASTGenerator { /** * Generate the SQLError struct, required when not used w/ Lighter. */ - func generateSQLError(name: String = "SQLError") -> Struct { - return Struct( - public: options.public, name: name, + func generateSQLError(name: String = "SQLError") -> TypeDefinition { + return TypeDefinition( + public: options.public, kind: .struct, name: name, conformances: [ .name("Swift.Error"), .name("Equatable"), .name("Sendable") ], diff --git a/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateRecordStructure.swift b/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateRecordStructure.swift index 47ddfc5..77e1f96 100644 --- a/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateRecordStructure.swift +++ b/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateRecordStructure.swift @@ -1,6 +1,6 @@ // // Created by Helge Heß. -// Copyright © 2022 ZeeZide GmbH. +// Copyright © 2022-2024 ZeeZide GmbH. // import LighterCodeGenAST @@ -14,9 +14,9 @@ extension EnlighterASTGenerator { name: name) } - func generateRecordStructure(for entity: EntityInfo) -> Struct { + func generateRecordStructure(for entity: EntityInfo) -> TypeDefinition { var idProperty : ComputedPropertyDefinition? = nil - var compoundPKey : Struct? + var compoundPKey : TypeDefinition? // MARK: - Primary Key @@ -31,11 +31,10 @@ extension EnlighterASTGenerator { } } - return Struct( - public : options.public, - name : entity.name, + return TypeDefinition( + public: options.public, kind: .struct, name: entity.name, conformances : conformancesForRecordType(entity).map { .name($0) }, - structures : compoundPKey.flatMap({ [ $0 ] }) ?? [], + nestedTypes : compoundPKey.flatMap({ [ $0 ] }) ?? [], typeVariables : [ .let("schema", is: .call(name: "Schema"), comment: @@ -186,10 +185,10 @@ extension EnlighterASTGenerator { ) } - func generateCompoundIDStruct(for entity: EntityInfo) -> Struct { + func generateCompoundIDStruct(for entity: EntityInfo) -> TypeDefinition { let pkeys = entity.primaryKeyProperties - return Struct( - public: options.public, name: "ID", + return TypeDefinition( + public: options.public, kind: .struct, name: "ID", conformances: [ .name("Swift.Hashable") ], variables: pkeys.map { property in .let(public: options.public, property.name, type(for: property), diff --git a/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateSchemaStructure.swift b/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateSchemaStructure.swift index 4e5e2df..575060e 100644 --- a/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateSchemaStructure.swift +++ b/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateSchemaStructure.swift @@ -94,8 +94,8 @@ extension EnlighterASTGenerator { } } - func generateSchemaStructure(for entity: EntityInfo) -> Struct { - var typeVariables = [ Struct.InstanceVariable ]() + func generateSchemaStructure(for entity: EntityInfo) -> TypeDefinition { + var typeVariables = [ TypeDefinition.InstanceVariable ]() // Type Variables @@ -282,11 +282,11 @@ extension EnlighterASTGenerator { )) } - return Struct( - public: options.public, name: api.recordSchemaName, - conformances: conformancesForSchemaType(entity).map { .name($0) }, - typeAliases: typeAliases, - typeVariables: typeVariables, + return TypeDefinition( + public: options.public, kind: .struct, name: api.recordSchemaName, + conformances : conformancesForSchemaType(entity).map { .name($0) }, + typeAliases : typeAliases, + typeVariables : typeVariables, variables: !options.useLighter ? [] : entity.properties.map { .let($0.name, is: makeMappedColumn(for: $0, in: entity), comment: diff --git a/Tests/CodeGenASTTests/BuilderTests.swift b/Tests/CodeGenASTTests/BuilderTests.swift index 5947119..a532293 100644 --- a/Tests/CodeGenASTTests/BuilderTests.swift +++ b/Tests/CodeGenASTTests/BuilderTests.swift @@ -36,9 +36,9 @@ final class BuilderTests: XCTestCase { } func testExtensionWithNestedStruct() { - let s = Struct(name: "RecordTypes") + let s = TypeDefinition(kind: .struct, name: "RecordTypes") let ext = Extension(extendedType: .name("SQLDatabaseFetchOperations"), - structures: [ s ]) + typeDefinitions: [ s ]) let unit = CompilationUnit(name: "GeneratedSelectOperations", extensions: [ ext ]) diff --git a/Tests/CodeGenASTTests/Fixtures.swift b/Tests/CodeGenASTTests/Fixtures.swift index 393b310..fb3dc51 100644 --- a/Tests/CodeGenASTTests/Fixtures.swift +++ b/Tests/CodeGenASTTests/Fixtures.swift @@ -93,8 +93,8 @@ enum Fixtures { ) } - static let personRecordStruct = Struct( - public: true, name: "Person", + static let personRecordStruct = TypeDefinition( + public: true, kind: .struct, name: "Person", conformances: [ .name("SQLKeyedTableRecord"), .name("Identifiable") ], typeVariables: [ .let("schema", is: .call(name: "Schema"), diff --git a/Tests/CodeGenASTTests/GenerationTests.swift b/Tests/CodeGenASTTests/GenerationTests.swift index 6d91615..badd5f2 100644 --- a/Tests/CodeGenASTTests/GenerationTests.swift +++ b/Tests/CodeGenASTTests/GenerationTests.swift @@ -1,6 +1,6 @@ // // Created by Helge Heß. -// Copyright © 2022 ZeeZide GmbH. +// Copyright © 2022-2024 ZeeZide GmbH. // import XCTest @@ -86,7 +86,7 @@ final class GenerationTests: XCTestCase { } func testEmptyPublicStruct() { - let s = Struct(public: true, name: "RecordTypes") + let s = TypeDefinition(public: true, kind: .struct, name: "RecordTypes") let source : String = { let builder = CodeGenerator() @@ -106,9 +106,9 @@ final class GenerationTests: XCTestCase { } func testExtensionWithStruct() { - let s = Struct(public: true, name: "RecordTypes") + let s = TypeDefinition(public: true, kind: .struct, name: "RecordTypes") let ext = Extension(extendedType: .name("SQLDatabaseFetchOperations"), - structures: [ s ]) + typeDefinitions: [ s ]) let source : String = { let builder = CodeGenerator() @@ -264,8 +264,8 @@ final class GenerationTests: XCTestCase { ) } - let schemaStruct = Struct( - public: true, name: "Schema", + let schemaStruct = TypeDefinition( + public: true, kind: .struct, name: "Schema", conformances: [ .name("SQLKeyedTableSchema"), .name("SQLSwiftMatchableSchema") ], typeAliases: [ @@ -420,7 +420,7 @@ final class GenerationTests: XCTestCase { ) let ext = Extension(extendedType: .name("Person"), - structures: [ schemaStruct ]) + typeDefinitions: [ schemaStruct ]) let source : String = { let builder = CodeGenerator() diff --git a/Tests/FiveThirtyEightTests/FiveThirtyEightTests.swift b/Tests/FiveThirtyEightTests/FiveThirtyEightTests.swift index 1646af9..c4c9c45 100644 --- a/Tests/FiveThirtyEightTests/FiveThirtyEightTests.swift +++ b/Tests/FiveThirtyEightTests/FiveThirtyEightTests.swift @@ -78,6 +78,6 @@ final class FiveThirtyEightTests: XCTestCase { print(" Extensions: #\(unit.extensions.count)") #endif - XCTAssertEqual(schema.tables.count + 1, unit.structures.count) + XCTAssertEqual(schema.tables.count + 1, unit.typeDefinitions.count) } } diff --git a/Tests/NorthwindTests/NorthwindTests.swift b/Tests/NorthwindTests/NorthwindTests.swift index 2296df4..4273f6f 100644 --- a/Tests/NorthwindTests/NorthwindTests.swift +++ b/Tests/NorthwindTests/NorthwindTests.swift @@ -104,7 +104,7 @@ final class NorthwindTests: XCTestCase { #endif XCTAssertEqual(schema.tables.count + schema.views.count + 1, - unit.structures.count) + unit.typeDefinitions.count) } func testCompoundPrimaryKeyGeneration() throws {