Skip to content

Commit 24a97e6

Browse files
committed
Merge branch 'main' into hd-naming-strategy-optimistic
2 parents 140f5c0 + 3ecfcfa commit 24a97e6

File tree

8 files changed

+333
-89
lines changed

8 files changed

+333
-89
lines changed

Package.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ let package = Package(
4949

5050
// General algorithms
5151
.package(url: "https://github.com/apple/swift-algorithms", from: "1.2.0"),
52+
.package(url: "https://github.com/apple/swift-collections", from: "1.1.4"),
5253

5354
// Read OpenAPI documents
5455
.package(url: "https://github.com/mattpolzin/OpenAPIKit", from: "3.3.0"),
@@ -72,7 +73,9 @@ let package = Package(
7273
.product(name: "OpenAPIKit", package: "OpenAPIKit"),
7374
.product(name: "OpenAPIKit30", package: "OpenAPIKit"),
7475
.product(name: "OpenAPIKitCompat", package: "OpenAPIKit"),
75-
.product(name: "Algorithms", package: "swift-algorithms"), .product(name: "Yams", package: "Yams"),
76+
.product(name: "Algorithms", package: "swift-algorithms"),
77+
.product(name: "OrderedCollections", package: "swift-collections"),
78+
.product(name: "Yams", package: "Yams"),
7679
],
7780
swiftSettings: swiftSettings
7881
),

Sources/_OpenAPIGeneratorCore/Parser/YamsParser.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ extension Diagnostic {
139139
static func openAPIMissingVersionError(location: Location) -> Diagnostic {
140140
error(
141141
message:
142-
"No openapi key found, please provide a valid OpenAPI document with OpenAPI versions in the 3.0.x or 3.1.x sets.",
142+
"No key named openapi found. Please provide a valid OpenAPI document with OpenAPI versions in the 3.0.x or 3.1.x sets.",
143143
location: location
144144
)
145145
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftOpenAPIGenerator open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
import OpenAPIKit
15+
import OrderedCollections
16+
17+
/// The backing type of a raw enum.
18+
enum RawEnumBackingType {
19+
20+
/// Backed by a `String`.
21+
case string
22+
23+
/// Backed by an `Int`.
24+
case integer
25+
}
26+
27+
/// The extracted enum value's identifier.
28+
private enum EnumCaseID: Hashable, CustomStringConvertible {
29+
30+
/// A string value.
31+
case string(String)
32+
33+
/// An integer value.
34+
case integer(Int)
35+
36+
var description: String {
37+
switch self {
38+
case .string(let value): return "\"\(value)\""
39+
case .integer(let value): return String(value)
40+
}
41+
}
42+
}
43+
44+
/// A wrapper for the metadata about the raw enum case.
45+
private struct EnumCase {
46+
47+
/// Used for checking uniqueness.
48+
var id: EnumCaseID
49+
50+
/// The raw Swift-safe name for the case.
51+
var caseName: String
52+
53+
/// The literal value of the enum case.
54+
var literal: LiteralDescription
55+
}
56+
57+
extension EnumCase: Equatable { static func == (lhs: EnumCase, rhs: EnumCase) -> Bool { lhs.id == rhs.id } }
58+
59+
extension EnumCase: Hashable { func hash(into hasher: inout Hasher) { hasher.combine(id) } }
60+
61+
extension FileTranslator {
62+
63+
/// Returns a declaration of the specified raw value-based enum schema.
64+
/// - Parameters:
65+
/// - backingType: The backing type of the enum.
66+
/// - typeName: The name of the type to give to the declared enum.
67+
/// - userDescription: A user-specified description from the OpenAPI
68+
/// document.
69+
/// - isNullable: Whether the enum schema is nullable.
70+
/// - allowedValues: The enumerated allowed values.
71+
/// - Throws: A `GenericError` if a disallowed value is encountered.
72+
/// - Returns: A declaration of the specified raw value-based enum schema.
73+
func translateRawEnum(
74+
backingType: RawEnumBackingType,
75+
typeName: TypeName,
76+
userDescription: String?,
77+
isNullable: Bool,
78+
allowedValues: [AnyCodable]
79+
) throws -> Declaration {
80+
var cases: OrderedSet<EnumCase> = []
81+
func addIfUnique(id: EnumCaseID, caseName: String) throws {
82+
let literal: LiteralDescription
83+
switch id {
84+
case .string(let string): literal = .string(string)
85+
case .integer(let int): literal = .int(int)
86+
}
87+
guard cases.append(.init(id: id, caseName: caseName, literal: literal)).inserted else {
88+
try diagnostics.emit(
89+
.warning(
90+
message: "Duplicate enum value, skipping",
91+
context: ["id": "\(id)", "foundIn": typeName.description]
92+
)
93+
)
94+
return
95+
}
96+
}
97+
for anyValue in allowedValues.map(\.value) {
98+
switch backingType {
99+
case .string:
100+
// In nullable enum schemas, empty strings are parsed as Void.
101+
// This is unlikely to be fixed, so handling that case here.
102+
// https://github.com/apple/swift-openapi-generator/issues/118
103+
if isNullable && anyValue is Void {
104+
try addIfUnique(id: .string(""), caseName: context.asSwiftSafeName(""))
105+
} else {
106+
guard let rawValue = anyValue as? String else {
107+
throw GenericError(message: "Disallowed value for a string enum '\(typeName)': \(anyValue)")
108+
}
109+
let caseName = context.asSwiftSafeName(rawValue)
110+
try addIfUnique(id: .string(rawValue), caseName: caseName)
111+
}
112+
case .integer:
113+
let rawValue: Int
114+
if let intRawValue = anyValue as? Int {
115+
rawValue = intRawValue
116+
} else if let stringRawValue = anyValue as? String, let intRawValue = Int(stringRawValue) {
117+
rawValue = intRawValue
118+
} else {
119+
throw GenericError(message: "Disallowed value for an integer enum '\(typeName)': \(anyValue)")
120+
}
121+
let caseName = rawValue < 0 ? "_n\(abs(rawValue))" : "_\(rawValue)"
122+
try addIfUnique(id: .integer(rawValue), caseName: caseName)
123+
}
124+
}
125+
let baseConformance: String
126+
switch backingType {
127+
case .string: baseConformance = Constants.RawEnum.baseConformanceString
128+
case .integer: baseConformance = Constants.RawEnum.baseConformanceInteger
129+
}
130+
let conformances = [baseConformance] + Constants.RawEnum.conformances
131+
return try translateRawRepresentableEnum(
132+
typeName: typeName,
133+
conformances: conformances,
134+
userDescription: userDescription,
135+
cases: cases.map { ($0.caseName, $0.literal) },
136+
unknownCaseName: nil,
137+
unknownCaseDescription: nil
138+
)
139+
}
140+
}

Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateStringEnum.swift

Lines changed: 0 additions & 86 deletions
This file was deleted.

Sources/swift-openapi-generator/Documentation.docc/Proposals/Proposals.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,5 @@ If you have any questions, tag [Honza Dvorsky](https://github.com/czechboy0) or
5252
- <doc:SOAR-0008>
5353
- <doc:SOAR-0009>
5454
- <doc:SOAR-0010>
55+
- <doc:SOAR-0011>
5556
- <doc:SOAR-0012>

0 commit comments

Comments
 (0)