Skip to content

Commit 58e72b0

Browse files
committed
Favour async getPackageMetadata overload
1 parent 2d95b6c commit 58e72b0

File tree

1 file changed

+127
-97
lines changed

1 file changed

+127
-97
lines changed

Sources/PackageRegistry/RegistryClient.swift

Lines changed: 127 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -151,17 +151,35 @@ public final class RegistryClient: Cancellable {
151151
observabilityScope: ObservabilityScope,
152152
callbackQueue: DispatchQueue
153153
) async throws -> PackageMetadata {
154-
try await withCheckedThrowingContinuation { continuation in
155-
self.getPackageMetadata(
156-
package: package,
154+
guard let registryIdentity = package.registry else {
155+
throw RegistryError.invalidPackageIdentity(package)
156+
}
157+
158+
guard let registry = self.configuration.registry(for: registryIdentity.scope) else {
159+
throw RegistryError.registryNotConfigured(scope: registryIdentity.scope)
160+
}
161+
162+
let underlying = {
163+
try await self._getPackageMetadata(
164+
registry: registry,
165+
package: registryIdentity,
157166
timeout: timeout,
158167
observabilityScope: observabilityScope,
159-
callbackQueue: callbackQueue,
160-
completion: {
161-
continuation.resume(with: $0)
162-
}
168+
callbackQueue: callbackQueue
163169
)
164170
}
171+
172+
if registry.supportsAvailability {
173+
// The only value that will return is .available, otherwise this check throws an error.
174+
if case .available = try await self.withAvailabilityCheck(
175+
registry: registry,
176+
observabilityScope: observabilityScope,
177+
callbackQueue: callbackQueue
178+
) {
179+
return try await underlying()
180+
}
181+
}
182+
return try await underlying()
165183
}
166184

167185
@available(*, noasync, message: "Use the async alternative")
@@ -173,43 +191,18 @@ public final class RegistryClient: Cancellable {
173191
completion: @escaping (Result<PackageMetadata, Error>) -> Void
174192
) {
175193
let completion = self.makeAsync(completion, on: callbackQueue)
176-
177-
guard let registryIdentity = package.registry else {
178-
return completion(.failure(RegistryError.invalidPackageIdentity(package)))
179-
}
180-
181-
guard let registry = self.configuration.registry(for: registryIdentity.scope) else {
182-
return completion(.failure(RegistryError.registryNotConfigured(scope: registryIdentity.scope)))
183-
}
184-
185-
observabilityScope.emit(debug: "registry for \(package): \(registry)")
186-
187-
let underlying = {
188-
_ = Task {
189-
let result = await self._getPackageMetadata(
190-
registry: registry,
191-
package: registryIdentity,
194+
_ = Task {
195+
do {
196+
let result = try await self.getPackageMetadata(
197+
package: package,
192198
timeout: timeout,
193199
observabilityScope: observabilityScope,
194200
callbackQueue: callbackQueue
195201
)
196-
completion(result)
197-
}
198-
}
199-
200-
if registry.supportsAvailability {
201-
self.withAvailabilityCheck(
202-
registry: registry,
203-
observabilityScope: observabilityScope,
204-
callbackQueue: callbackQueue
205-
) { error in
206-
if let error {
207-
return completion(.failure(error))
208-
}
209-
underlying()
202+
completion(.success(result))
203+
} catch {
204+
completion(.failure(error))
210205
}
211-
} else {
212-
underlying()
213206
}
214207
}
215208

@@ -220,18 +213,18 @@ public final class RegistryClient: Cancellable {
220213
timeout: DispatchTimeInterval?,
221214
observabilityScope: ObservabilityScope,
222215
callbackQueue: DispatchQueue
223-
) async -> Result<PackageMetadata, Error> {
216+
) async throws -> PackageMetadata {
224217
guard var components = URLComponents(url: registry.url, resolvingAgainstBaseURL: true) else {
225-
return .failure(RegistryError.invalidURL(registry.url))
218+
throw RegistryError.invalidURL(registry.url)
226219
}
227220
components.appendPathComponents("\(package.scope)", "\(package.name)")
228221
guard let url = components.url else {
229-
return .failure(RegistryError.invalidURL(registry.url))
222+
throw RegistryError.invalidURL(registry.url)
230223
}
231224

232225
// If the responses are paginated then iterate until we've exasuasted all the pages and have a full versions list.
233-
func iterateResponses(url: URL, existingMetadata: PackageMetadata) async -> Result<PackageMetadata, Error> {
234-
let response = await self._getIndividualPackageMetadata(
226+
func iterateResponses(url: URL, existingMetadata: PackageMetadata) async throws -> PackageMetadata {
227+
let metadata = try await self._getIndividualPackageMetadata(
235228
url: url,
236229
registry: registry,
237230
package: package,
@@ -240,32 +233,38 @@ public final class RegistryClient: Cancellable {
240233
callbackQueue: callbackQueue
241234
)
242235

243-
if case .success(let metadata) = response {
244-
let mergedMetadata = PackageMetadata(
236+
let mergedMetadata = PackageMetadata(
237+
registry: registry,
238+
versions: existingMetadata.versions + metadata.versions,
239+
alternateLocations: existingMetadata.alternateLocations.count > 0
240+
? existingMetadata.alternateLocations
241+
: metadata.alternateLocations,
242+
nextPage: metadata.nextPage
243+
)
244+
if let nextPage = mergedMetadata.nextPage?.url {
245+
return try await iterateResponses(
246+
url: nextPage,
247+
existingMetadata: mergedMetadata
248+
)
249+
} else {
250+
return PackageMetadata(
245251
registry: registry,
246-
versions: existingMetadata.versions + metadata.versions,
247-
alternateLocations: existingMetadata.alternateLocations.count > 0
248-
? existingMetadata.alternateLocations
249-
: metadata.alternateLocations,
250-
nextPage: metadata.nextPage
252+
versions: mergedMetadata.versions.sorted(by: >),
253+
alternateLocations: mergedMetadata.alternateLocations,
254+
nextPage: mergedMetadata.nextPage
251255
)
252-
if let nextPage = mergedMetadata.nextPage?.url {
253-
return await iterateResponses(url: nextPage, existingMetadata: mergedMetadata)
254-
} else {
255-
return .success(
256-
PackageMetadata(
257-
registry: registry,
258-
versions: mergedMetadata.versions.sorted(by: >),
259-
alternateLocations: mergedMetadata.alternateLocations,
260-
nextPage: mergedMetadata.nextPage
261-
)
262-
)
263-
}
264256
}
265-
return response
266257
}
267258

268-
return await iterateResponses(url: url, existingMetadata: PackageMetadata(registry: registry, versions: [], alternateLocations: [], nextPage: nil))
259+
return try await iterateResponses(
260+
url: url,
261+
existingMetadata: PackageMetadata(
262+
registry: registry,
263+
versions: [],
264+
alternateLocations: [],
265+
nextPage: nil
266+
)
267+
)
269268
}
270269

271270
private func _getIndividualPackageMetadata(
@@ -275,50 +274,59 @@ public final class RegistryClient: Cancellable {
275274
timeout: DispatchTimeInterval?,
276275
observabilityScope: ObservabilityScope,
277276
callbackQueue: DispatchQueue
278-
) async -> Result<PackageMetadata, Error> {
277+
) async throws -> PackageMetadata {
278+
let start = DispatchTime.now()
279+
observabilityScope.emit(info: "retrieving \(package) metadata from \(url)")
280+
281+
let response: LegacyHTTPClient.Response
279282
do {
280-
let start = DispatchTime.now()
281-
observabilityScope.emit(info: "retrieving \(package) metadata from \(url)")
282-
let response = try await self.httpClient.get(
283+
response = try await self.httpClient.get(
283284
url,
284285
headers: [
285286
"Accept": self.acceptHeader(mediaType: .json),
286287
],
287288
options: self.defaultRequestOptions(timeout: timeout, callbackQueue: callbackQueue),
288289
observabilityScope: observabilityScope
289290
)
291+
} catch {
292+
throw RegistryError.failedRetrievingReleases(registry: registry, package: package.underlying, error: error)
293+
}
294+
observabilityScope
295+
.emit(
296+
debug: "server response for \(url): \(response.statusCode) in \(start.distance(to: .now()).descriptionInSeconds)"
297+
)
290298

291-
observabilityScope
292-
.emit(
293-
debug: "server response for \(url): \(response.statusCode) in \(start.distance(to: .now()).descriptionInSeconds)"
294-
)
295-
296-
switch response.statusCode {
297-
case 200:
298-
let packageMetadata = try response.parseJSON(
299-
Serialization.PackageMetadata.self,
300-
decoder: self.jsonDecoder
301-
)
299+
switch response.statusCode {
300+
case 200:
301+
let packageMetadata = try response.parseJSON(
302+
Serialization.PackageMetadata.self,
303+
decoder: self.jsonDecoder
304+
)
302305

303-
let versions = packageMetadata.releases.filter { $0.value.problem == nil }
304-
.compactMap { Version($0.key) }
306+
let versions = packageMetadata.releases.filter { $0.value.problem == nil }
307+
.compactMap { Version($0.key) }
305308

306-
let alternateLocations = response.headers.parseAlternativeLocationLinks()
307-
let paginationLinks = response.headers.parsePagniationLinks()
309+
let alternateLocations = response.headers.parseAlternativeLocationLinks()
310+
let paginationLinks = response.headers.parsePagniationLinks()
308311

309-
return .success(PackageMetadata(
310-
registry: registry,
311-
versions: versions,
312-
alternateLocations: alternateLocations.map(\.url),
313-
nextPage: paginationLinks.first { $0.kind == .next }?.url
314-
))
315-
case 404:
316-
return .failure(RegistryError.failedRetrievingReleases(registry: registry, package: package.underlying, error: RegistryError.packageNotFound))
317-
default:
318-
return .failure(RegistryError.failedRetrievingReleases(registry: registry, package: package.underlying, error: self.unexpectedStatusError(response, expectedStatus: [200, 404])))
319-
}
320-
} catch {
321-
return .failure(RegistryError.failedRetrievingReleases(registry: registry, package: package.underlying, error: error))
312+
return PackageMetadata(
313+
registry: registry,
314+
versions: versions,
315+
alternateLocations: alternateLocations.map(\.url),
316+
nextPage: paginationLinks.first { $0.kind == .next }?.url
317+
)
318+
case 404:
319+
throw RegistryError.failedRetrievingReleases(
320+
registry: registry,
321+
package: package.underlying,
322+
error: RegistryError.packageNotFound
323+
)
324+
default:
325+
throw RegistryError.failedRetrievingReleases(
326+
registry: registry,
327+
package: package.underlying,
328+
error: self.unexpectedStatusError(response, expectedStatus: [200, 404])
329+
)
322330
}
323331
}
324332

@@ -1811,6 +1819,28 @@ public final class RegistryClient: Cancellable {
18111819
}
18121820
}
18131821
1822+
private func withAvailabilityCheck(
1823+
registry: Registry,
1824+
observabilityScope: ObservabilityScope,
1825+
callbackQueue: DispatchQueue
1826+
) async throws -> AvailabilityStatus {
1827+
try await withCheckedThrowingContinuation { continuation in
1828+
self.withAvailabilityCheck(
1829+
registry: registry,
1830+
observabilityScope: observabilityScope,
1831+
callbackQueue: callbackQueue,
1832+
next: {
1833+
if let error = $0 {
1834+
continuation.resume(throwing: error)
1835+
} else {
1836+
continuation.resume(returning: .available)
1837+
}
1838+
}
1839+
)
1840+
}
1841+
}
1842+
1843+
@available(*, noasync, message: "Use the async alternative")
18141844
private func withAvailabilityCheck(
18151845
registry: Registry,
18161846
observabilityScope: ObservabilityScope,

0 commit comments

Comments
 (0)