@@ -151,17 +151,35 @@ public final class RegistryClient: Cancellable {
151
151
observabilityScope: ObservabilityScope ,
152
152
callbackQueue: DispatchQueue
153
153
) 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,
157
166
timeout: timeout,
158
167
observabilityScope: observabilityScope,
159
- callbackQueue: callbackQueue,
160
- completion: {
161
- continuation. resume ( with: $0)
162
- }
168
+ callbackQueue: callbackQueue
163
169
)
164
170
}
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( )
165
183
}
166
184
167
185
@available( * , noasync, message: " Use the async alternative " )
@@ -173,43 +191,18 @@ public final class RegistryClient: Cancellable {
173
191
completion: @escaping ( Result < PackageMetadata , Error > ) -> Void
174
192
) {
175
193
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 ,
192
198
timeout: timeout,
193
199
observabilityScope: observabilityScope,
194
200
callbackQueue: callbackQueue
195
201
)
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) )
210
205
}
211
- } else {
212
- underlying ( )
213
206
}
214
207
}
215
208
@@ -220,18 +213,18 @@ public final class RegistryClient: Cancellable {
220
213
timeout: DispatchTimeInterval? ,
221
214
observabilityScope: ObservabilityScope,
222
215
callbackQueue: DispatchQueue
223
- ) async -> Result < PackageMetadata , Error > {
216
+ ) async throws -> PackageMetadata {
224
217
guard var components = URLComponents ( url: registry. url, resolvingAgainstBaseURL: true ) else {
225
- return . failure ( RegistryError . invalidURL ( registry. url) )
218
+ throw RegistryError . invalidURL ( registry. url)
226
219
}
227
220
components. appendPathComponents ( " \( package . scope) " , " \( package . name) " )
228
221
guard let url = components. url else {
229
- return . failure ( RegistryError . invalidURL ( registry. url) )
222
+ throw RegistryError . invalidURL ( registry. url)
230
223
}
231
224
232
225
// 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 (
235
228
url: url,
236
229
registry: registry,
237
230
package : package ,
@@ -240,32 +233,38 @@ public final class RegistryClient: Cancellable {
240
233
callbackQueue: callbackQueue
241
234
)
242
235
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 (
245
251
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
251
255
)
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
- }
264
256
}
265
- return response
266
257
}
267
258
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
+ )
269
268
}
270
269
271
270
private func _getIndividualPackageMetadata(
@@ -275,50 +274,59 @@ public final class RegistryClient: Cancellable {
275
274
timeout: DispatchTimeInterval? ,
276
275
observabilityScope: ObservabilityScope,
277
276
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
279
282
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 (
283
284
url,
284
285
headers: [
285
286
" Accept " : self . acceptHeader ( mediaType: . json) ,
286
287
] ,
287
288
options: self . defaultRequestOptions ( timeout: timeout, callbackQueue: callbackQueue) ,
288
289
observabilityScope: observabilityScope
289
290
)
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
+ )
290
298
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
+ )
302
305
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) }
305
308
306
- let alternateLocations = response. headers. parseAlternativeLocationLinks ( )
307
- let paginationLinks = response. headers. parsePagniationLinks ( )
309
+ let alternateLocations = response. headers. parseAlternativeLocationLinks ( )
310
+ let paginationLinks = response. headers. parsePagniationLinks ( )
308
311
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
+ )
322
330
}
323
331
}
324
332
@@ -1811,6 +1819,28 @@ public final class RegistryClient: Cancellable {
1811
1819
}
1812
1820
}
1813
1821
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 " )
1814
1844
private func withAvailabilityCheck(
1815
1845
registry: Registry,
1816
1846
observabilityScope: ObservabilityScope,
0 commit comments