|
1 | 1 | /* |
2 | 2 | This source file is part of the Swift.org open source project |
3 | 3 |
|
4 | | - Copyright (c) 2021-2024 Apple Inc. and the Swift project authors |
| 4 | + Copyright (c) 2021-2025 Apple Inc. and the Swift project authors |
5 | 5 | Licensed under Apache License v2.0 with Runtime Library Exception |
6 | 6 |
|
7 | 7 | See https://swift.org/LICENSE.txt for license information |
@@ -44,68 +44,68 @@ public struct ConvertService: DocumentationService { |
44 | 44 | _ message: DocumentationServer.Message, |
45 | 45 | completion: @escaping (DocumentationServer.Message) -> () |
46 | 46 | ) { |
47 | | - let conversionResult = retrievePayload(message) |
48 | | - .flatMap(decodeRequest) |
49 | | - .flatMap(convert) |
50 | | - .flatMap(encodeResponse) |
51 | | - |
52 | | - switch conversionResult { |
53 | | - case .success(let response): |
54 | | - completion( |
55 | | - DocumentationServer.Message( |
56 | | - type: Self.convertResponseMessageType, |
57 | | - identifier: "\(message.identifier)-response", |
58 | | - payload: response |
| 47 | + Task { |
| 48 | + let result = await process(message) |
| 49 | + completion(result) |
| 50 | + } |
| 51 | + } |
| 52 | + |
| 53 | + public func process(_ message: DocumentationServer.Message) async -> DocumentationServer.Message { |
| 54 | + func makeErrorResponse(_ error: ConvertServiceError) -> DocumentationServer.Message { |
| 55 | + DocumentationServer.Message( |
| 56 | + type: Self.convertResponseErrorMessageType, |
| 57 | + identifier: "\(message.identifier)-response-error", |
| 58 | + |
| 59 | + // Force trying because encoding known messages should never fail. |
| 60 | + payload: try! JSONEncoder().encode(error) |
| 61 | + ) |
| 62 | + } |
| 63 | + |
| 64 | + guard let payload = message.payload else { |
| 65 | + return makeErrorResponse(.missingPayload()) |
| 66 | + } |
| 67 | + |
| 68 | + let request: ConvertRequest |
| 69 | + do { |
| 70 | + request = try JSONDecoder().decode(ConvertRequest.self, from: payload) |
| 71 | + } catch { |
| 72 | + return makeErrorResponse(.invalidRequest(underlyingError: error.localizedDescription)) |
| 73 | + } |
| 74 | + |
| 75 | + let renderNodes: [RenderNode] |
| 76 | + let renderReferenceStore: RenderReferenceStore? |
| 77 | + do { |
| 78 | + (renderNodes, renderReferenceStore) = try await convert(request: request, messageIdentifier: message.identifier) |
| 79 | + } catch { |
| 80 | + return makeErrorResponse(.conversionError(underlyingError: error.localizedDescription)) |
| 81 | + } |
| 82 | + |
| 83 | + do { |
| 84 | + let encoder = JSONEncoder() |
| 85 | + let encodedResponse = try encoder.encode( |
| 86 | + try ConvertResponse( |
| 87 | + renderNodes: renderNodes.map(encoder.encode), |
| 88 | + renderReferenceStore: renderReferenceStore.map(encoder.encode) |
59 | 89 | ) |
60 | 90 | ) |
61 | 91 |
|
62 | | - case .failure(let error): |
63 | | - completion( |
64 | | - DocumentationServer.Message( |
65 | | - type: Self.convertResponseErrorMessageType, |
66 | | - identifier: "\(message.identifier)-response-error", |
67 | | - |
68 | | - // Force trying because encoding known messages should never fail. |
69 | | - payload: try! JSONEncoder().encode(error) |
70 | | - ) |
| 92 | + return DocumentationServer.Message( |
| 93 | + type: Self.convertResponseMessageType, |
| 94 | + identifier: "\(message.identifier)-response", |
| 95 | + payload: encodedResponse |
71 | 96 | ) |
72 | | - } |
73 | | - } |
74 | | - |
75 | | - /// Attempts to retrieve the payload from the given message, returning a failure if the payload is missing. |
76 | | - /// |
77 | | - /// - Returns: A result with the message's payload if present, otherwise a ``ConvertServiceError/missingPayload`` |
78 | | - /// failure. |
79 | | - private func retrievePayload( |
80 | | - _ message: DocumentationServer.Message |
81 | | - ) -> Result<(payload: Data, messageIdentifier: String), ConvertServiceError> { |
82 | | - message.payload.map { .success(($0, message.identifier)) } ?? .failure(.missingPayload()) |
83 | | - } |
84 | | - |
85 | | - /// Attempts to decode the given request, returning a failure if decoding failed. |
86 | | - /// |
87 | | - /// - Returns: A result with the decoded request if the decoding succeeded, otherwise a |
88 | | - /// ``ConvertServiceError/invalidRequest`` failure. |
89 | | - private func decodeRequest( |
90 | | - data: Data, |
91 | | - messageIdentifier: String |
92 | | - ) -> Result<(request: ConvertRequest, messageIdentifier: String), ConvertServiceError> { |
93 | | - Result { |
94 | | - return (try JSONDecoder().decode(ConvertRequest.self, from: data), messageIdentifier) |
95 | | - }.mapErrorToConvertServiceError { |
96 | | - .invalidRequest(underlyingError: $0.localizedDescription) |
| 97 | + } catch { |
| 98 | + return makeErrorResponse(.invalidResponseMessage(underlyingError: error.localizedDescription)) |
97 | 99 | } |
98 | 100 | } |
99 | 101 |
|
100 | 102 | /// Attempts to process the given convert request, returning a failure if the conversion failed. |
101 | 103 | /// |
102 | | - /// - Returns: A result with the produced render nodes if the conversion was successful, otherwise a |
103 | | - /// ``ConvertServiceError/conversionError`` failure. |
| 104 | + /// - Returns: A result with the produced render nodes if the conversion was successful |
104 | 105 | private func convert( |
105 | 106 | request: ConvertRequest, |
106 | 107 | messageIdentifier: String |
107 | | - ) -> Result<([RenderNode], RenderReferenceStore?), ConvertServiceError> { |
108 | | - Result { |
| 108 | + ) async throws -> ([RenderNode], RenderReferenceStore?) { |
109 | 109 | // Update DocC's current feature flags based on the ones provided |
110 | 110 | // in the request. |
111 | 111 | FeatureFlags.current = request.featureFlags |
@@ -155,7 +155,7 @@ public struct ConvertService: DocumentationService { |
155 | 155 | (bundle, dataProvider) = Self.makeBundleAndInMemoryDataProvider(request) |
156 | 156 | } |
157 | 157 |
|
158 | | - let context = try DocumentationContext(bundle: bundle, dataProvider: dataProvider, configuration: configuration) |
| 158 | + let context = try await DocumentationContext(bundle: bundle, dataProvider: dataProvider, configuration: configuration) |
159 | 159 |
|
160 | 160 | // Precompute the render context |
161 | 161 | let renderContext = RenderContext(documentationContext: context, bundle: bundle) |
@@ -228,30 +228,6 @@ public struct ConvertService: DocumentationService { |
228 | 228 | } |
229 | 229 |
|
230 | 230 | return (renderNodes, referenceStore) |
231 | | - }.mapErrorToConvertServiceError { |
232 | | - .conversionError(underlyingError: $0.localizedDescription) |
233 | | - } |
234 | | - } |
235 | | - |
236 | | - /// Encodes a conversion response to send to the client. |
237 | | - /// |
238 | | - /// - Parameter renderNodes: The render nodes that were produced as part of the conversion. |
239 | | - private func encodeResponse( |
240 | | - renderNodes: [RenderNode], |
241 | | - renderReferenceStore: RenderReferenceStore? |
242 | | - ) -> Result<Data, ConvertServiceError> { |
243 | | - Result { |
244 | | - let encoder = JSONEncoder() |
245 | | - |
246 | | - return try encoder.encode( |
247 | | - try ConvertResponse( |
248 | | - renderNodes: renderNodes.map(encoder.encode), |
249 | | - renderReferenceStore: renderReferenceStore.map(encoder.encode) |
250 | | - ) |
251 | | - ) |
252 | | - }.mapErrorToConvertServiceError { |
253 | | - .invalidResponseMessage(underlyingError: $0.localizedDescription) |
254 | | - } |
255 | 231 | } |
256 | 232 |
|
257 | 233 | /// Takes a base reference store and adds uncurated article references and documentation extensions. |
@@ -297,25 +273,6 @@ public struct ConvertService: DocumentationService { |
297 | 273 | } |
298 | 274 | } |
299 | 275 |
|
300 | | -extension Result { |
301 | | - /// Returns a new result, mapping any failure value using the given transformation if the error is not a conversion error. |
302 | | - /// |
303 | | - /// If the error value is a ``ConvertServiceError``, it is returned as-is. If it's not, the given transformation is called on the |
304 | | - /// error. |
305 | | - /// |
306 | | - /// - Parameter transform: A closure that takes the failure value of the instance. |
307 | | - func mapErrorToConvertServiceError( |
308 | | - _ transform: (any Error) -> ConvertServiceError |
309 | | - ) -> Result<Success, ConvertServiceError> { |
310 | | - mapError { error in |
311 | | - switch error { |
312 | | - case let error as ConvertServiceError: return error |
313 | | - default: return transform(error) |
314 | | - } |
315 | | - } |
316 | | - } |
317 | | -} |
318 | | - |
319 | 276 | private extension SymbolGraph.LineList.Line { |
320 | 277 | /// Creates a line given a convert request line. |
321 | 278 | init(_ line: ConvertRequest.Line) { |
|
0 commit comments