Skip to content

Use variadic generics in Swift 5.9 #341

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Aug 25, 2023
2 changes: 2 additions & 0 deletions Sources/PostgresNIO/New/PostgresRow-multi-decode.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// NOTE: THIS FILE IS AUTO-GENERATED BY dev/generate-postgresrow-multi-decode.sh

#if compiler(<5.9)
extension PostgresRow {
@inlinable
@_alwaysEmitIntoClient
Expand Down Expand Up @@ -1171,3 +1172,4 @@ extension PostgresRow {
try self.decode((T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14).self, context: .default, file: file, line: line)
}
}
#endif
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// NOTE: THIS FILE IS AUTO-GENERATED BY dev/generate-postgresrowsequence-multi-decode.sh

#if canImport(_Concurrency)
#if compiler(<5.9)
extension AsyncSequence where Element == PostgresRow {
@inlinable
@_alwaysEmitIntoClient
Expand Down
174 changes: 174 additions & 0 deletions Sources/PostgresNIO/New/VariadicGenerics.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#if compiler(>=5.9)
extension PostgresRow {
// --- snip TODO: Remove once bug is fixed, that disallows tuples of one
@inlinable
public func decode<Column: PostgresDecodable>(
_: Column.Type,
file: String = #fileID,
line: Int = #line
) throws -> (Column) {
try self.decode(Column.self, context: .default, file: file, line: line)
}

@inlinable
public func decode<Column: PostgresDecodable>(
_: Column.Type,
context: PostgresDecodingContext<some PostgresJSONDecoder>,
file: String = #fileID,
line: Int = #line
) throws -> (Column) {
precondition(self.columns.count >= 1)
let columnIndex = 0
var cellIterator = self.data.makeIterator()
var cellData = cellIterator.next().unsafelyUnwrapped
var columnIterator = self.columns.makeIterator()
let column = columnIterator.next().unsafelyUnwrapped
let swiftTargetType: Any.Type = Column.self

do {
let r0 = try Column._decodeRaw(from: &cellData, type: column.dataType, format: column.format, context: context)

return (r0)
} catch let code as PostgresDecodingError.Code {
throw PostgresDecodingError(
code: code,
columnName: column.name,
columnIndex: columnIndex,
targetType: swiftTargetType,
postgresType: column.dataType,
postgresFormat: column.format,
postgresData: cellData,
file: file,
line: line
)
}
}
// --- snap TODO: Remove once bug is fixed, that disallows tuples of one

@inlinable
public func decode<each Column: PostgresDecodable>(
_ columnType: (repeat each Column).Type,
context: PostgresDecodingContext<some PostgresJSONDecoder>,
file: String = #fileID,
line: Int = #line
) throws -> (repeat each Column) {
let packCount = ComputeParameterPackLength.count(ofPack: repeat (each Column).self)
precondition(self.columns.count >= packCount)

var columnIndex = 0
var cellIterator = self.data.makeIterator()
var columnIterator = self.columns.makeIterator()

return (
repeat try Self.decodeNextColumn(
(each Column).self,
cellIterator: &cellIterator,
columnIterator: &columnIterator,
columnIndex: &columnIndex,
context: context,
file: file,
line: line
)
)
}

@inlinable
static func decodeNextColumn<Column: PostgresDecodable>(
_ columnType: Column.Type,
cellIterator: inout IndexingIterator<DataRow>,
columnIterator: inout IndexingIterator<[RowDescription.Column]>,
columnIndex: inout Int,
context: PostgresDecodingContext<some PostgresJSONDecoder>,
file: String,
line: Int
) throws -> Column {
defer { columnIndex += 1 }

let column = columnIterator.next().unsafelyUnwrapped
var cellData = cellIterator.next().unsafelyUnwrapped
do {
return try Column._decodeRaw(from: &cellData, type: column.dataType, format: column.format, context: context)
} catch let code as PostgresDecodingError.Code {
throw PostgresDecodingError(
code: code,
columnName: column.name,
columnIndex: columnIndex,
targetType: Column.self,
postgresType: column.dataType,
postgresFormat: column.format,
postgresData: cellData,
file: file,
line: line
)
}
}

@inlinable
public func decode<each Column: PostgresDecodable>(
_ columnType: (repeat each Column).Type,
file: String = #fileID,
line: Int = #line
) throws -> (repeat each Column) {
try self.decode(columnType, context: .default, file: file, line: line)
}
}

extension AsyncSequence where Element == PostgresRow {
// --- snip TODO: Remove once bug is fixed, that disallows tuples of one
@inlinable
public func decode<Column: PostgresDecodable>(
_: Column.Type,
context: PostgresDecodingContext<some PostgresJSONDecoder>,
file: String = #fileID,
line: Int = #line
) -> AsyncThrowingMapSequence<Self, (Column)> {
self.map { row in
try row.decode(Column.self, context: context, file: file, line: line)
}
}

@inlinable
public func decode<Column: PostgresDecodable>(
_: Column.Type,
file: String = #fileID,
line: Int = #line
) -> AsyncThrowingMapSequence<Self, (Column)> {
self.decode(Column.self, context: .default, file: file, line: line)
}
// --- snap TODO: Remove once bug is fixed, that disallows tuples of one

public func decode<each Column: PostgresDecodable>(
_ columnType: (repeat each Column).Type,
context: PostgresDecodingContext<some PostgresJSONDecoder>,
file: String = #fileID,
line: Int = #line
) -> AsyncThrowingMapSequence<Self, (repeat each Column)> {
self.map { row in
try row.decode(columnType, context: context, file: file, line: line)
}
}

public func decode<each Column: PostgresDecodable>(
_ columnType: (repeat each Column).Type,
file: String = #fileID,
line: Int = #line
) -> AsyncThrowingMapSequence<Self, (repeat each Column)> {
self.decode(columnType, context: .default, file: file, line: line)
}
}

@usableFromInline
enum ComputeParameterPackLength {
@usableFromInline
enum BoolConverter<T> {
@usableFromInline
typealias Bool = Swift.Bool
}

@inlinable
static func count<each T>(ofPack t: repeat each T) -> Int {
MemoryLayout<(repeat BoolConverter<each T>.Bool)>.size / MemoryLayout<Bool>.stride
}
}
#endif // compiler(>=5.9)

7 changes: 4 additions & 3 deletions Tests/IntegrationTests/AsyncTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import NIOPosix
import NIOCore

final class AsyncPostgresConnectionTests: XCTestCase {

func test1kRoundTrips() async throws {
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) }
Expand Down Expand Up @@ -37,7 +36,8 @@ final class AsyncPostgresConnectionTests: XCTestCase {
try await withTestConnection(on: eventLoop) { connection in
let rows = try await connection.query("SELECT generate_series(\(start), \(end));", logger: .psqlTest)
var counter = 0
for try await element in rows.decode(Int.self, context: .default) {
for try await row in rows {
let element = try row.decode(Int.self)
XCTAssertEqual(element, counter + 1)
counter += 1
}
Expand Down Expand Up @@ -259,7 +259,8 @@ final class AsyncPostgresConnectionTests: XCTestCase {
try await withTestConnection(on: eventLoop) { connection in
let rows = try await connection.query("SELECT generate_series(\(start), \(end));", logger: .psqlTest)
var counter = 1
for try await element in rows.decode(Int.self, context: .default) {
for try await row in rows {
let element = try row.decode(Int.self, context: .default)
XCTAssertEqual(element, counter)
counter += 1
}
Expand Down
8 changes: 4 additions & 4 deletions Tests/IntegrationTests/PostgresNIOTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1246,10 +1246,10 @@ final class PostgresNIOTests: XCTestCase {
return EventLoopFuture.whenAllSucceed([a, b, c], on: self.eventLoop)
}).wait())
XCTAssertEqual(queries?.count, 3)
var resutIterator = queries?.makeIterator()
XCTAssertEqual(try resutIterator?.next()?.first?.decode(String.self, context: .default), "a")
XCTAssertEqual(try resutIterator?.next()?.first?.decode(String.self, context: .default), "b")
XCTAssertEqual(try resutIterator?.next()?.first?.decode(String.self, context: .default), "c")
var resultIterator = queries?.makeIterator()
XCTAssertEqual(try resultIterator?.next()?.first?.decode(String.self, context: .default), "a")
XCTAssertEqual(try resultIterator?.next()?.first?.decode(String.self, context: .default), "b")
XCTAssertEqual(try resultIterator?.next()?.first?.decode(String.self, context: .default), "c")
}

// https://github.com/vapor/postgres-nio/issues/122
Expand Down
12 changes: 6 additions & 6 deletions Tests/PostgresNIOTests/New/PostgresRowSequenceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ final class PostgresRowSequenceTests: XCTestCase {

var counter = 0
for try await row in rowSequence {
XCTAssertEqual(try row.decode(Int.self, context: .default), counter)
XCTAssertEqual(try row.decode(Int.self), counter)
counter += 1

if counter == 64 {
Expand Down Expand Up @@ -135,7 +135,7 @@ final class PostgresRowSequenceTests: XCTestCase {

var counter = 0
for try await row in rowSequence {
XCTAssertEqual(try row.decode(Int.self, context: .default), counter)
XCTAssertEqual(try row.decode(Int.self), counter)
counter += 1
}

Expand Down Expand Up @@ -163,7 +163,7 @@ final class PostgresRowSequenceTests: XCTestCase {

var counter = 0
for try await row in rowSequence {
XCTAssertEqual(try row.decode(Int.self, context: .default), counter)
XCTAssertEqual(try row.decode(Int.self), counter)
counter += 1
}

Expand Down Expand Up @@ -220,7 +220,7 @@ final class PostgresRowSequenceTests: XCTestCase {
}

let row1 = try await rowIterator.next()
XCTAssertEqual(try row1?.decode(Int.self, context: .default), 0)
XCTAssertEqual(try row1?.decode(Int.self), 0)

DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
stream.receive(completion: .success("SELECT 1"))
Expand Down Expand Up @@ -252,7 +252,7 @@ final class PostgresRowSequenceTests: XCTestCase {
}

let row1 = try await rowIterator.next()
XCTAssertEqual(try row1?.decode(Int.self, context: .default), 0)
XCTAssertEqual(try row1?.decode(Int.self), 0)

DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
stream.receive(completion: .failure(PSQLError.serverClosedConnection(underlying: nil)))
Expand Down Expand Up @@ -415,7 +415,7 @@ final class PostgresRowSequenceTests: XCTestCase {
var counter = 1
for _ in 0..<(2 * messagePerChunk - 1) {
let row = try await rowIterator.next()
XCTAssertEqual(try row?.decode(Int.self, context: .default), counter)
XCTAssertEqual(try row?.decode(Int.self), counter)
counter += 1
}

Expand Down