Skip to content

Commit 30a947d

Browse files
committed
@timestamp properties can be declared
1 parent b393ae0 commit 30a947d

File tree

12 files changed

+208
-97
lines changed

12 files changed

+208
-97
lines changed

Sources/ViiLibrary/Column.swift

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,63 @@
1+
import Foundation
2+
13
public struct Column: ViiColumn, Codable, Equatable {
24
public var columnName: String
35
public var dataType: String
46
public var isNullable: Bool
7+
8+
var swiftDataType: String {
9+
return SQLType(self.dataType).swiftType
10+
}
11+
12+
var swiftVariableName: String {
13+
return self.columnName.format().lowerCasedFirstLetter()
14+
}
15+
16+
func isTimestamp() -> Bool {
17+
if SQLType.timestampable.contains(SQLType(self.dataType)) { return true }
18+
return false
19+
}
20+
21+
func getPropertyWrapper() -> String {
22+
if isTimestamp() {
23+
let triggerEnumCase = self.getTimestampEnumeration()
24+
return "@Timestamp(key: \"\(self.columnName)\", on: \(triggerEnumCase))"
25+
}
26+
return "@Field(key: \"\(self.columnName)\")"
27+
}
28+
29+
func getPropertyDeclaration() -> String {
30+
let propertyDataType = SQLType.potsgresArray.contains(SQLType(self.dataType)) ? "[\(self.swiftDataType)]" : self.swiftDataType
31+
let isNullable = self.isNullable || self.isTimestamp() ? "?" : ""
32+
return "var \(self.swiftVariableName): \(propertyDataType)\(isNullable)"
33+
}
34+
35+
private func getTimestampEnumeration() -> String {
36+
let deleted = NSRegularExpression("(deleted|del)")
37+
let updated = NSRegularExpression("(update|mod)")
38+
if deleted.matches(self.columnName.lowercased()) {
39+
return ".delete"
40+
} else if updated.matches(self.columnName.lowercased()) {
41+
return ".update"
42+
}
43+
return ".create"
44+
}
545
}
46+
47+
extension NSRegularExpression {
48+
convenience init(_ pattern: String) {
49+
do {
50+
try self.init(pattern: pattern)
51+
} catch {
52+
preconditionFailure("Illegal regular expression: \(pattern).")
53+
}
54+
}
55+
}
56+
57+
extension NSRegularExpression {
58+
func matches(_ string: String) -> Bool {
59+
let range = NSRange(location: 0, length: string.utf16.count)
60+
return firstMatch(in: string, options: [], range: range) != nil
61+
}
62+
}
63+

Sources/ViiLibrary/Extensions/String+NamingConvention.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ extension String {
33
return self.split{ !$0.isLetter }
44
}
55

6-
/// Removes non alpha chars
7-
func stripped() -> String {
8-
return self.split{ !$0.isLetter }.joined()
9-
}
6+
// /// Removes non alpha chars
7+
// func stripped() -> String {
8+
// return self.split{ !$0.isLetter }.joined()
9+
// }
1010

1111
/// formats common naming conventions to Swift class naming conventions where possible
1212
func format() -> String {

Sources/ViiLibrary/FileContents.swift

Lines changed: 32 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ public struct FileContents {
22

33
var originalTableName: String
44
var columns: [Column]
5-
var primaryKey: Column?
5+
var primaryKey: PrimaryKey?
66
var foreignKeys: [ForeignKey]
77

8-
init(originalTableName: String, columns: [Column], primaryKey: Column?, foreignKeys: [ForeignKey]) {
8+
init(originalTableName: String, columns: [Column], primaryKey: PrimaryKey?, foreignKeys: [ForeignKey]) {
99
self.originalTableName = originalTableName
1010
self.columns = columns
1111
self.primaryKey = primaryKey
@@ -23,22 +23,27 @@ public struct FileContents {
2323
}
2424
return ""
2525
}
26+
2627
/// gets swift naming convention ClassName
2728
public var className: String {
2829
return self.originalTableName.format()
2930
}
31+
3032
/// gets class name declaration
3133
var classDeclaration: String {
3234
return "final class \(self.className): Model, Content {"
3335
}
36+
3437
/// gets the schema declaration
3538
var schema: String {
3639
return "static let schema = \"\(self.originalTableName)\""
3740
}
41+
3842
/// gets the fromatted schema
3943
var schemaFormatted: String {
4044
return "\n\t\(self.schema)\n"
4145
}
46+
4247
/// gets closing declaration
4348
var endDeclaration: String {
4449
return "\n}"
@@ -47,7 +52,7 @@ public struct FileContents {
4752
/// gets property wrapper for primary key
4853
var primaryKeyWrapper: String? {
4954
if let pk = self.primaryKey {
50-
return self.getPropertyWrapper(column: pk, isPrimary: true, isForeign: false)
55+
return self.getPropertyWrapper(column: pk)
5156
}
5257
return nil
5358
}
@@ -77,8 +82,8 @@ public struct FileContents {
7782
var foreignKeyDeclarations: String? {
7883
if self.foreignKeys.isEmpty { return nil }
7984
return self.foreignKeys.map { fk in
80-
let propertyWrapper = self.getPropertyWrapper(column: fk, isPrimary: false, isForeign: true)
81-
let property = self.getForeignKeyPropertyDeclaration(column: fk)
85+
let propertyWrapper = self.getPropertyWrapper(column: fk)
86+
let property = self.getPropertyDeclaration(column: fk)
8287
return "\n\n\t" + propertyWrapper + "\n\t" + property
8388
}.joined()
8489
}
@@ -95,11 +100,11 @@ public struct FileContents {
95100
var trimmedColumns: [Column] {
96101
var processedColumns:[Column] = []
97102
if let pk = self.primaryKey {
98-
processedColumns.append(pk)
103+
processedColumns.append(pk.toColumn)
99104
}
100105
if !self.foreignKeys.isEmpty {
101106
processedColumns += self.foreignKeys.compactMap{ fk in
102-
return fk.convertToColumn()
107+
return fk.toColumn
103108
}
104109
}
105110
return self.columns.filter { !processedColumns.contains($0) }
@@ -108,9 +113,9 @@ public struct FileContents {
108113
/// gets declarations for remaining columns
109114
var columnProperties: String? {
110115
if self.trimmedColumns.isEmpty { return nil }
111-
return self.trimmedColumns.map { col in
112-
let propertyWrapper = self.getPropertyWrapper(column: col, isPrimary: false, isForeign: false)
113-
let property = self.getPropertyDeclaration(column: col)
116+
return self.trimmedColumns.map { column in
117+
let propertyWrapper = self.getPropertyWrapper(column: column)
118+
let property = self.getPropertyDeclaration(column: column)
114119
return "\n\t" + propertyWrapper + "\n\t" + property + "\n"
115120
}.joined()
116121
}
@@ -123,35 +128,17 @@ public struct FileContents {
123128
return ""
124129
}
125130

126-
/// returns propertyWrapper for column
127-
func getPropertyWrapper<T>(column: T, isPrimary: Bool, isForeign: Bool) -> String where T: ViiColumn {
128-
if isPrimary {
129-
return "@ID(key: \"\(column.columnName)\")"
130-
}
131-
if isForeign {
132-
if column.isNullable {
133-
return "@OptionalParent(key: \"\(column.columnName)\")"
134-
}
135-
return "@Parent(key: \"\(column.columnName)\")"
136-
}
137-
if SQLType.timestampable.contains(SQLType(column.dataType)){
138-
return "@Timestamp(key: \"\(column.columnName)\")"
139-
}
140-
return "@Field(key: \"\(column.columnName)\")"
131+
132+
/// Takes a `ViiColumn` returns the property wrapper declaration
133+
/// - Parameter column: `ViiColumn`
134+
/// - returns: `String` representation of `@propertyWrapper`
135+
func getPropertyWrapper<T>(column: T) -> String where T: ViiColumn {
136+
return column.getPropertyWrapper()
141137
}
142138

143139
/// returns property declartion and optionality
144-
func getPropertyDeclaration(column: Column) -> String {
145-
let dataType = SQLType(column.dataType).swiftType
146-
let isNullable = column.isNullable ? "?" : ""
147-
return "var \(column.columnName.format().lowerCasedFirstLetter()): \(dataType)\(isNullable)"
148-
}
149-
150-
func getForeignKeyPropertyDeclaration(column: ForeignKey) -> String {
151-
let isNullable = column.isNullable ? "?" : ""
152-
let formattedVar = column.columnName.format().lowerCasedFirstLetter()
153-
let tableReference = column.constrainedTable.format()
154-
return "var \(formattedVar): \(tableReference)\(isNullable)"
140+
func getPropertyDeclaration<T>(column: T) -> String where T: ViiColumn {
141+
return column.getPropertyDeclaration()
155142
}
156143

157144
func getInitializer() -> String {
@@ -161,29 +148,22 @@ public struct FileContents {
161148
/// gets the full initializer
162149
func getFullInitializer() -> String {
163150
let initial = "\n\n\tinit("
164-
let args = self.columns.map { col in
165-
let dataType = SQLType(col.dataType).swiftType
166-
let optionality = col.isNullable ? "?," : ","
167-
return " \(col.columnName.format().lowerCasedFirstLetter()): \(dataType)\(optionality)"
151+
let args = self.columns.map { column in
152+
var propertyType = column.swiftDataType
153+
if column.isNullable {
154+
propertyType += "? = nil"
155+
}
156+
return " \(column.swiftVariableName): \(propertyType),"
168157
}.joined()
169-
let assignment = self.columns.map { col in
170-
return "\n\t\tself." + col.columnName.format().lowerCasedFirstLetter() + " = " + col.columnName.format().lowerCasedFirstLetter()
158+
let assignment = self.columns.map { column in
159+
return "\n\t\tself." + column.swiftVariableName + " = " + column.swiftVariableName
171160
}.joined()
172161
return initial + args.dropLast() + "){" + assignment + "\n\t}"
173162
}
174163

175164
/// returns the file contents
176165
public func getFileContents() -> String {
177-
return "import Vapor\n"
178-
+ imports
179-
+ classDeclaration
180-
+ schemaFormatted
181-
+ primaryKeyDeclarationFormatted
182-
+ foreignKeyDeclarationsFormatted
183-
+ columnDeclarationsFormatted
184-
+ getInitializer()
185-
+ getFullInitializer()
186-
+ endDeclaration
166+
return "import Fluent\nimport Vapor\n\(imports) \(classDeclaration) \(schemaFormatted) \(primaryKeyDeclarationFormatted) \(foreignKeyDeclarationsFormatted) \(columnDeclarationsFormatted) \(getInitializer()) \(getFullInitializer()) \(endDeclaration)"
187167
}
188168
}
189169

Sources/ViiLibrary/ForeignKey.swift

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,27 @@ public struct ForeignKey: ViiColumn, Codable {
44
public var isNullable: Bool
55
public var constrainedTable: String
66

7-
func convertToColumn() -> Column {
7+
var swiftVariableName: String {
8+
return self.columnName.format().lowerCasedFirstLetter()
9+
}
10+
11+
var swiftModelReference: String {
12+
return self.constrainedTable.format()
13+
}
14+
15+
var toColumn: Column {
816
return Column(columnName: self.columnName, dataType: self.dataType, isNullable: self.isNullable)
917
}
18+
19+
func getPropertyWrapper() -> String {
20+
if self.isNullable {
21+
return "@OptionalParent(key: \"\(self.columnName)\")"
22+
}
23+
return "@Parent(key: \"\(self.columnName)\")"
24+
}
25+
26+
func getPropertyDeclaration() -> String {
27+
let isNullable = self.isNullable ? "?" : ""
28+
return "var \(self.swiftVariableName): \(self.swiftModelReference)\(isNullable)"
29+
}
1030
}

Sources/ViiLibrary/GenerateFile.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
public struct GenerateFile {
22

3-
public static func generateFileContents(table: Table, connection: ViiConnection) throws -> FileContents {
3+
public static func generateFileContents(table: Table, connection: ViiConnection) throws -> FileContents {
44

55
let columns = try connection.getColumns(table: table).wait()
66
let primaryKey = try connection.getPrimaryKey(table: table).wait()
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
public struct PrimaryKey: ViiColumn, Codable, Equatable {
2+
public var columnName: String
3+
public var dataType: String
4+
public var isNullable: Bool
5+
6+
var swiftVariableName: String {
7+
return self.columnName.format().lowerCasedFirstLetter()
8+
}
9+
10+
var toColumn: Column {
11+
return Column(columnName: self.columnName, dataType: self.dataType, isNullable: self.isNullable)
12+
}
13+
14+
func getPropertyWrapper() -> String {
15+
return "@ID(key: \"\(self.columnName)\")"
16+
}
17+
18+
func getPropertyDeclaration() -> String {
19+
let dataType = SQLType(self.dataType).swiftType
20+
return "var \(self.swiftVariableName): \(dataType)?"
21+
}
22+
}

Sources/ViiLibrary/Protocols/ViiColumn.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,9 @@ protocol ViiColumn {
22
var columnName: String { get }
33
var dataType: String { get }
44
var isNullable: Bool { get }
5+
6+
var swiftVariableName: String { get }
7+
8+
func getPropertyWrapper() -> String
9+
func getPropertyDeclaration() -> String
510
}
Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
import NIO
22

3+
34
public protocol ViiConnection: AnyObject {
45

56
/// gets all tables for specified database
67
func getTables() -> EventLoopFuture<[Table]>
7-
/// closes current db connection group
8+
9+
/// closes DB connection
810
func close()
9-
/// gets columns and types for a table
11+
12+
/// Returns columns for a given DB table
13+
/// - Parameter table: `Table` SQL Table
1014
func getColumns(table: Table) -> EventLoopFuture<[Column]>
11-
/// get primary keys
12-
func getPrimaryKey(table: Table) -> EventLoopFuture<Column?>
13-
/// get foreign keys
15+
16+
/// Returns primary keys for given DB table
17+
/// - Parameter table: `Table` SQL Table
18+
func getPrimaryKey(table: Table) -> EventLoopFuture<PrimaryKey?>
19+
20+
/// Returns foreign keys for a given DB table
21+
/// - Parameter table: `Table` SQL Table
1422
func getForeignKeys(table: Table) -> EventLoopFuture<[ForeignKey]>
1523
}

0 commit comments

Comments
 (0)