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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
@testable import AsyncDataLoader
import XCTest

@testable import AsyncDataLoader

let sleepConstant = UInt64(2_000_000)

actor Concurrent<T> {
Expand DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
@testable import AsyncDataLoader
import XCTest

@testable import AsyncDataLoader

let sleepConstant = UInt64(2_000_000)

actor Concurrent<T> {
Expand DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
@testable import AsyncDataLoader
import XCTest

@testable import AsyncDataLoader

let sleepConstant = UInt64(2_000_000)

actor Concurrent<T> {
Expand DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
@testable import AsyncDataLoader
import XCTest

@testable import AsyncDataLoader

let sleepConstant = UInt64(2_000_000)

actor Concurrent<T> {
Expand DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
@testable import AsyncDataLoader
import XCTest

@testable import AsyncDataLoader

let sleepConstant = UInt64(2_000_000)

actor Concurrent<T> {
Expand DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
@testable import AsyncDataLoader
import XCTest

@testable import AsyncDataLoader

let sleepConstant = UInt64(2_000_000)

actor Concurrent<T> {
Expand DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
@testable import AsyncDataLoader
import XCTest

@testable import AsyncDataLoader

let sleepConstant = UInt64(2_000_000)

actor Concurrent<T> {
Expand DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
@testable import AsyncDataLoader
import XCTest

@testable import AsyncDataLoader

let sleepConstant = UInt64(2_000_000)

actor Concurrent<T> {
Expand DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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