-
Notifications
You must be signed in to change notification settings - Fork 126
/
Copy pathDiagnostics.swift
264 lines (234 loc) · 9.11 KB
/
Diagnostics.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftOpenAPIGenerator open source project
//
// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import Foundation
import OpenAPIKit30
/// A message emitted by the generator.
public struct Diagnostic: Error, Codable {
/// Describes the severity of a diagnostic.
public enum Severity: String, Codable {
/// An informative message, does not represent an issue.
case note
/// A non-fatal issue that should be addressed, but the generator is
/// able to recover.
case warning
/// A fatal issue from which the generator cannot recover.
case error
}
/// The severity of the diagnostic.
public var severity: Severity
/// A user-friendly description of the diagnostic.
public var message: String
/// Additional information about where the issue occurred.
public var context: [String: String] = [:]
/// Creates an informative message, which doesn't represent an issue.
public static func note(message: String, context: [String: String] = [:]) -> Diagnostic {
.init(severity: .note, message: message, context: context)
}
/// Creates a recoverable issue, which doesn't prevent the generator
/// from continuing.
/// - Parameters:
/// - message: The message that describes the warning.
/// - context: A set of key-value pairs that help the user understand
/// where the warning occurred.
/// - Returns: A warning diagnostic.
public static func warning(
message: String,
context: [String: String] = [:]
) -> Diagnostic {
.init(severity: .warning, message: message, context: context)
}
/// Creates a non-recoverable issue, which leads the generator to stop.
/// - Parameters:
/// - message: The message that describes the error.
/// - context: A set of key-value pairs that help the user understand
/// where the warning occurred.
/// - Returns: An error diagnostic.
public static func error(
message: String,
context: [String: String] = [:]
) -> Diagnostic {
.init(severity: .error, message: message, context: context)
}
/// Creates a diagnostic for an unsupported feature.
///
/// Recoverable, the generator skips the unsupported feature.
/// - Parameters:
/// - feature: A human-readable name of the feature.
/// - foundIn: A description of the location in which the unsupported
/// feature was detected.
/// - context: A set of key-value pairs that help the user understand
/// where the warning occurred.
/// - Returns: A warning diagnostic.
public static func unsupported(
_ feature: String,
foundIn: String,
context: [String: String] = [:]
) -> Diagnostic {
var context = context
context["foundIn"] = foundIn
return warning(message: "Feature \"\(feature)\" is not supported, skipping", context: context)
}
}
extension Diagnostic.Severity: CustomStringConvertible {
public var description: String {
rawValue
}
}
extension Diagnostic: CustomStringConvertible {
public var description: String {
let contextString = context.map { "\($0)=\($1)" }.sorted().joined(separator: ", ")
return "\(severity): \(message) [\(contextString.isEmpty ? "" : "context: \(contextString)")]"
}
}
extension Diagnostic: LocalizedError {
public var errorDescription: String? {
description
}
}
/// A type that receives diagnostics.
///
/// The collector can process, log, or store the diagnostics.
///
/// See concrete implementations for several variants of the collector.
public protocol DiagnosticCollector {
/// Submits a diagnostic to the collector.
/// - Parameter diagnostic: The diagnostic to submit.
func emit(_ diagnostic: Diagnostic)
}
extension DiagnosticCollector {
/// Emits a diagnostic for an unsupported feature found in the specified
/// string location.
///
/// Recoverable, the generator skips the unsupported feature.
/// - Parameters:
/// - feature: A human-readable name of the feature.
/// - foundIn: A description of the location in which the unsupported
/// feature was detected.
/// - context: A set of key-value pairs that help the user understand
/// where the warning occurred.
func emitUnsupported(
_ feature: String,
foundIn: String,
context: [String: String] = [:]
) {
emit(Diagnostic.unsupported(feature, foundIn: foundIn, context: context))
}
/// Emits a diagnostic for an unsupported feature found in the specified
/// type name.
///
/// Recoverable, the generator skips the unsupported feature.
/// - Parameters:
/// - feature: A human-readable name of the feature.
/// - foundIn: The type name related to where the issue was detected.
/// - context: A set of key-value pairs that help the user understand
/// where the warning occurred.
func emitUnsupported(
_ feature: String,
foundIn: TypeName,
context: [String: String] = [:]
) {
emit(Diagnostic.unsupported(feature, foundIn: foundIn.description, context: context))
}
/// Emits a diagnostic for an unsupported feature found in the specified
/// string location when the test closure returns a non-nil value.
///
/// Recoverable, the generator skips the unsupported feature.
/// - Parameters:
/// - test: A closure that returns a non-nil value when an unsupported
/// feature is specified in the OpenAPI document.
/// - feature: A human-readable name of the feature.
/// - foundIn: A description of the location in which the unsupported
/// feature was detected.
/// - context: A set of key-value pairs that help the user understand
/// where the warning occurred.
func emitUnsupportedIfNotNil(
_ test: Any?,
_ feature: String,
foundIn: String,
context: [String: String] = [:]
) {
if test == nil {
return
}
emitUnsupported(feature, foundIn: foundIn, context: context)
}
/// Emits a diagnostic for an unsupported feature found in the specified
/// string location when the test collection is not empty.
///
/// Recoverable, the generator skips the unsupported feature.
/// - Parameters:
/// - test: A collection that is not empty if the unsupported feature
/// is specified in the OpenAPI document.
/// - feature: A human-readable name of the feature.
/// - foundIn: A description of the location in which the unsupported
/// feature was detected.
/// - context: A set of key-value pairs that help the user understand
/// where the warning occurred.
func emitUnsupportedIfNotEmpty<C: Collection>(
_ test: C?,
_ feature: String,
foundIn: String,
context: [String: String] = [:]
) {
guard let test = test, !test.isEmpty else {
return
}
emitUnsupported(feature, foundIn: foundIn, context: context)
}
/// Emits a diagnostic for an unsupported feature found in the specified
/// string location when the test Boolean value is true.
///
/// Recoverable, the generator skips the unsupported feature.
/// - Parameters:
/// - test: A Boolean value that indicates whether the unsupported
/// feature is specified in the OpenAPI document.
/// - feature: A human-readable name of the feature.
/// - foundIn: A description of the location in which the unsupported
/// feature was detected.
/// - context: A set of key-value pairs that help the user understand
/// where the warning occurred.
func emitUnsupportedIfTrue(
_ test: Bool,
_ feature: String,
foundIn: String,
context: [String: String] = [:]
) {
if !test {
return
}
emitUnsupported(feature, foundIn: foundIn, context: context)
}
}
/// A diagnostic collector that prints diagnostics to standard output.
struct PrintingDiagnosticCollector: DiagnosticCollector {
/// Creates a new collector.
public init() {}
public func emit(_ diagnostic: Diagnostic) {
print(diagnostic.description)
}
}
/// A diagnostic collector that prints diagnostics to standard error.
public struct StdErrPrintingDiagnosticCollector: DiagnosticCollector {
/// Creates a new collector.
public init() {}
public func emit(_ diagnostic: Diagnostic) {
print(diagnostic.description, to: &stdErrHandle)
}
}
/// A no-op collector, silently ignores all diagnostics.
///
/// Useful when diagnostics can be ignored.
struct QuietDiagnosticCollector: DiagnosticCollector {
func emit(_ diagnostic: Diagnostic) {}
}