Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .swift-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"version": 1,
"indentation" : {
"spaces" : 4
},
"lineBreakBeforeEachArgument": true
}
4 changes: 2 additions & 2 deletions Sources/AsyncDataLoader/Channel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ extension Channel {
try await withCheckedThrowingContinuation { continuation in
Task {
switch result {
case let .success(success):
case .success(let success):
continuation.resume(returning: success)
case let .failure(failure):
case .failure(let failure):
continuation.resume(throwing: failure)
case nil:
waiters.append(continuation)
Expand Down
23 changes: 10 additions & 13 deletions Sources/AsyncDataLoader/DataLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,16 @@ public actor DataLoader<Key: Hashable & Sendable, Value: Sendable> {
let results = try await self.batchLoadFunction([key])

if results.isEmpty {
await channel
.fail(
DataLoaderError
.noValueForKey("Did not return value for key: \(key)")
)
await channel.fail(
DataLoaderError.noValueForKey("Did not return value for key: \(key)")
)
} else {
let result = results[0]

switch result {
case let .success(value):
case .success(let value):
await channel.fulfill(value)
case let .failure(error):
case .failure(let error):
await channel.fail(error)
}
}
Expand Down Expand Up @@ -198,19 +196,18 @@ public actor DataLoader<Key: Hashable & Sendable, Value: Sendable> {
let values = try await batchLoadFunction(keys)

if values.count != keys.count {
throw DataLoaderError
.typeError(
"The function did not return an array of the same length as the array of keys. \nKeys count: \(keys.count)\nValues count: \(values.count)"
)
throw DataLoaderError.typeError(
"The function did not return an array of the same length as the array of keys. \nKeys count: \(keys.count)\nValues count: \(values.count)"
)
}

for entry in batch.enumerated() {
let result = values[entry.offset]

switch result {
case let .failure(error):
case .failure(let error):
await entry.element.channel.fail(error)
case let .success(value):
case .success(let value):
await entry.element.channel.fulfill(value)
}
}
Expand Down
53 changes: 29 additions & 24 deletions Sources/DataLoader/DataLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,16 @@ public final class DataLoader<Key: Hashable, Value> {
do {
_ = try batchLoadFunction([key]).map { results in
if results.isEmpty {
promise
.fail(
DataLoaderError
.noValueForKey("Did not return value for key: \(key)")
promise.fail(
DataLoaderError.noValueForKey(
"Did not return value for key: \(key)"
)
)
} else {
let result = results[0]
switch result {
case let .success(value): promise.succeed(value)
case let .failure(error): promise.fail(error)
case .success(let value): promise.succeed(value)
case .failure(let error): promise.fail(error)
}
}
}
Expand Down Expand Up @@ -179,10 +179,10 @@ public final class DataLoader<Key: Hashable, Value> {
// If a maxBatchSize was provided and the queue is longer, then segment the
// queue into multiple batches, otherwise treat the queue as a single batch.
if let maxBatchSize = options.maxBatchSize, maxBatchSize > 0, maxBatchSize < batch.count {
for i in 0 ... (batch.count / maxBatchSize) {
for i in 0...(batch.count / maxBatchSize) {
let startIndex = i * maxBatchSize
let endIndex = (i + 1) * maxBatchSize
let slicedBatch = batch[startIndex ..< min(endIndex, batch.count)]
let slicedBatch = batch[startIndex..<min(endIndex, batch.count)]
try executeBatch(batch: Array(slicedBatch))
}
} else {
Expand All @@ -202,18 +202,17 @@ public final class DataLoader<Key: Hashable, Value> {
do {
_ = try batchLoadFunction(keys).flatMapThrowing { values in
if values.count != keys.count {
throw DataLoaderError
.typeError(
"The function did not return an array of the same length as the array of keys. \nKeys count: \(keys.count)\nValues count: \(values.count)"
)
throw DataLoaderError.typeError(
"The function did not return an array of the same length as the array of keys. \nKeys count: \(keys.count)\nValues count: \(values.count)"
)
}

for entry in batch.enumerated() {
let result = values[entry.offset]

switch result {
case let .failure(error): entry.element.promise.fail(error)
case let .success(value): entry.element.promise.succeed(value)
case .failure(let error): entry.element.promise.fail(error)
case .success(let value): entry.element.promise.succeed(value)
}
}
}.recover { error in
Expand All @@ -238,25 +237,30 @@ public final class DataLoader<Key: Hashable, Value> {
public typealias ConcurrentBatchLoadFunction<Key, Value> =
@Sendable (_ keys: [Key]) async throws -> [DataLoaderFutureValue<Value>]

public extension DataLoader {
extension DataLoader {
@available(macOS 12, iOS 15, watchOS 8, tvOS 15, *)
convenience init(
public convenience init(
on eventLoop: EventLoop,
options: DataLoaderOptions<Key, Value> = DataLoaderOptions(),
throwing asyncThrowingLoadFunction: @escaping ConcurrentBatchLoadFunction<Key, Value>
) {
self.init(options: options, batchLoadFunction: { keys in
let promise = eventLoop.next().makePromise(of: [DataLoaderFutureValue<Value>].self)
promise.completeWithTask {
try await asyncThrowingLoadFunction(keys)
self.init(
options: options,
batchLoadFunction: { keys in
let promise = eventLoop.next().makePromise(
of: [DataLoaderFutureValue<Value>].self
)
promise.completeWithTask {
try await asyncThrowingLoadFunction(keys)
}
return promise.futureResult
}
return promise.futureResult
})
)
}

/// Asynchronously loads a key, returning the value represented by that key.
@available(macOS 12, iOS 15, watchOS 8, tvOS 15, *)
func load(key: Key, on eventLoopGroup: EventLoopGroup) async throws -> Value {
public func load(key: Key, on eventLoopGroup: EventLoopGroup) async throws -> Value {
try await load(key: key, on: eventLoopGroup).get()
}

Expand All @@ -274,7 +278,8 @@ public final class DataLoader<Key: Hashable, Value> {
/// let aAndB = try await a + b
/// ```
@available(macOS 12, iOS 15, watchOS 8, tvOS 15, *)
func loadMany(keys: [Key], on eventLoopGroup: EventLoopGroup) async throws -> [Value] {
public func loadMany(keys: [Key], on eventLoopGroup: EventLoopGroup) async throws -> [Value]
{
try await loadMany(keys: keys, on: eventLoopGroup).get()
}
}
Expand Down
7 changes: 4 additions & 3 deletions Tests/AsyncDataLoaderTests/DataLoaderAbuseTests.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@testable import AsyncDataLoader
import XCTest

@testable import AsyncDataLoader

/// Provides descriptive error messages for API abuse
class DataLoaderAbuseTests: XCTestCase {
func testFuntionWithNoValues() async throws {
Expand All @@ -24,7 +25,7 @@ class DataLoaderAbuseTests: XCTestCase {
}

func testBatchFuntionMustPromiseAnArrayOfCorrectLength() async {
let identityLoader = DataLoader<Int, Int>() { _ in
let identityLoader = DataLoader<Int, Int> { _ in
[]
}

Expand All @@ -42,7 +43,7 @@ class DataLoaderAbuseTests: XCTestCase {
}

func testBatchFuntionWithSomeValues() async throws {
let identityLoader = DataLoader<Int, Int>() { keys in
let identityLoader = DataLoader<Int, Int> { keys in
var results = [DataLoaderValue<Int>]()

for key in keys {
Expand Down
9 changes: 5 additions & 4 deletions Tests/AsyncDataLoaderTests/DataLoaderTests.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@testable import AsyncDataLoader
import XCTest

@testable import AsyncDataLoader

let sleepConstant = UInt64(2_000_000)

actor Concurrent<T> {
Expand Down Expand Up @@ -39,7 +40,7 @@ final class DataLoaderTests: XCTestCase {

/// Supports loading multiple keys in one call
func testLoadingMultipleKeys() async throws {
let identityLoader = DataLoader<Int, Int>() { keys in
let identityLoader = DataLoader<Int, Int> { keys in
keys.map { DataLoaderValue.success($0) }
}

Expand Down Expand Up @@ -618,7 +619,7 @@ final class DataLoaderTests: XCTestCase {
var didFailWithErrorText2 = ""

switch didFailWithError2 {
case let .typeError(text):
case .typeError(let text):
didFailWithErrorText2 = text
case .noValueForKey:
break
Expand Down Expand Up @@ -648,7 +649,7 @@ final class DataLoaderTests: XCTestCase {
var didFailWithErrorText3 = ""

switch didFailWithError3 {
case let .typeError(text):
case .typeError(let text):
didFailWithErrorText3 = text
case .noValueForKey:
break
Expand Down
7 changes: 4 additions & 3 deletions Tests/DataLoaderTests/DataLoaderAbuseTests.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
@testable import DataLoader
import NIOPosix
import XCTest

@testable import DataLoader

/// Provides descriptive error messages for API abuse
class DataLoaderAbuseTests: XCTestCase {
func testFuntionWithNoValues() throws {
Expand Down Expand Up @@ -30,7 +31,7 @@ class DataLoaderAbuseTests: XCTestCase {
XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully())
}

let identityLoader = DataLoader<Int, Int>() { _ in
let identityLoader = DataLoader<Int, Int> { _ in
eventLoopGroup.next().makeSucceededFuture([])
}

Expand All @@ -48,7 +49,7 @@ class DataLoaderAbuseTests: XCTestCase {
XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully())
}

let identityLoader = DataLoader<Int, Int>() { keys in
let identityLoader = DataLoader<Int, Int> { keys in
var results = [DataLoaderFutureValue<Int>]()

for key in keys {
Expand Down
3 changes: 2 additions & 1 deletion Tests/DataLoaderTests/DataLoaderAsyncTests.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
@testable import DataLoader
import NIOPosix
import XCTest

@testable import DataLoader

#if compiler(>=5.5) && canImport(_Concurrency)

@available(macOS 12, iOS 15, watchOS 8, tvOS 15, *)
Expand Down
5 changes: 3 additions & 2 deletions Tests/DataLoaderTests/DataLoaderTests.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
@testable import DataLoader
import NIOCore
import NIOPosix
import XCTest

@testable import DataLoader

/// Primary API
final class DataLoaderTests: XCTestCase {
/// Builds a really really simple data loader'
Expand Down Expand Up @@ -32,7 +33,7 @@ final class DataLoaderTests: XCTestCase {
XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully())
}

let identityLoader = DataLoader<Int, Int>() { keys in
let identityLoader = DataLoader<Int, Int> { keys in
let results = keys.map { DataLoaderFutureValue.success($0) }

return eventLoopGroup.next().makeSucceededFuture(results)
Expand Down
Loading