Skip to content

Commit bd0d0fd

Browse files
Added TypeName and TypeUsage from OpenAPI Generator. (#1726)
Motivation: The types are used by the code renderer that will be brought over from OpenAPI Generator to the new gRPC Swift CodeGenLib. Modifications: Copied the TypeName, TypeUsage and some TypeName extensions that will be used in the context of gRPC, and modified TypeName so it represents only the Swift path of a type. Result: We will be able to bring over the Code Renderer and we will have types representations that can be used by the service translator.
1 parent 319cdde commit bd0d0fd

File tree

3 files changed

+340
-1
lines changed

3 files changed

+340
-1
lines changed

NOTICES.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,11 @@ This product uses derivations of swift-extras/swift-extras-base64 'Base64.swift'
6060

6161
---
6262

63-
This product uses derivations of apple/swift-openapi-generator 'StructuredSwiftRepresentation.swift'.
63+
This product uses derivations of apple/swift-openapi-generator 'StructuredSwiftRepresentation.swift',
64+
'TypeName.swift', 'TypeUsage.swift' and 'Builtins.swift'.
6465

6566
* LICENSE (Apache License 2.0):
6667
* https://github.com/apple/swift-openapi-generator/blob/main/LICENSE.txt
6768
* HOMEPAGE:
6869
* https://github.com/apple/swift-openapi-generator
70+
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright 2023, gRPC Authors All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
//===----------------------------------------------------------------------===//
17+
//
18+
// This source file is part of the SwiftOpenAPIGenerator open source project
19+
//
20+
// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors
21+
// Licensed under Apache License v2.0
22+
//
23+
// See LICENSE.txt for license information
24+
// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors
25+
//
26+
// SPDX-License-Identifier: Apache-2.0
27+
//
28+
//===----------------------------------------------------------------------===//
29+
import Foundation
30+
31+
/// A fully-qualified type name that contains the components of the Swift
32+
/// type name.
33+
///
34+
/// Use the type name to define a type, see also `TypeUsage` when referring
35+
/// to a type.
36+
struct TypeName: Hashable {
37+
/// A list of components that make up the type name.
38+
private let components: [String]
39+
40+
/// Creates a new type name with the specified list of components.
41+
/// - Parameter components: A list of components for the type.
42+
init(components: [String]) {
43+
precondition(!components.isEmpty, "TypeName path cannot be empty")
44+
self.components = components
45+
}
46+
47+
/// A string representation of the fully qualified Swift type name.
48+
///
49+
/// For example: `Swift.Int`.
50+
var fullyQualifiedName: String { components.joined(separator: ".") }
51+
52+
/// A string representation of the last path component of the Swift
53+
/// type name.
54+
///
55+
/// For example: `Int`.
56+
var shortName: String { components.last! }
57+
58+
/// Returns a type name by appending the specified component to the
59+
/// current type name.
60+
///
61+
/// In other words, returns a type name for a child type.
62+
/// - Parameters:
63+
/// - component: The name of the Swift type component.
64+
/// - Returns: A new type name.
65+
func appending(component: String) -> Self {
66+
return .init(components: components + [component])
67+
}
68+
69+
/// Returns a type name by removing the last component from the current
70+
/// type name.
71+
///
72+
/// In other words, returns a type name for the parent type.
73+
var parent: TypeName {
74+
precondition(components.count >= 1, "Cannot get the parent of a root type")
75+
return .init(components: components.dropLast())
76+
}
77+
}
78+
79+
extension TypeName: CustomStringConvertible {
80+
var description: String {
81+
return fullyQualifiedSwiftName
82+
}
83+
}
84+
85+
extension TypeName {
86+
/// Returns the type name for the String type.
87+
static var string: Self { .swift("String") }
88+
89+
/// Returns the type name for the Int type.
90+
static var int: Self { .swift("Int") }
91+
92+
/// Returns a type name for a type with the specified name in the
93+
/// Swift module.
94+
/// - Parameter name: The name of the type.
95+
/// - Returns: A TypeName representing the specified type within the Swift module.
96+
static func swift(_ name: String) -> TypeName { TypeName(components: ["Swift", name]) }
97+
98+
/// Returns a type name for a type with the specified name in the
99+
/// Foundation module.
100+
/// - Parameter name: The name of the type.
101+
/// - Returns: A TypeName representing the specified type within the Foundation module.
102+
static func foundation(_ name: String) -> TypeName {
103+
TypeName(components: ["Foundation", name])
104+
}
105+
}
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
/*
2+
* Copyright 2023, gRPC Authors All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
//===----------------------------------------------------------------------===//
17+
//
18+
// This source file is part of the SwiftOpenAPIGenerator open source project
19+
//
20+
// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors
21+
// Licensed under Apache License v2.0
22+
//
23+
// See LICENSE.txt for license information
24+
// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors
25+
//
26+
// SPDX-License-Identifier: Apache-2.0
27+
//
28+
//===----------------------------------------------------------------------===//
29+
30+
/// A reference to a Swift type, including modifiers such as whether the
31+
/// type is wrapped in an optional, an array, or a dictionary.
32+
///
33+
/// Whenever unsure whether to use `TypeUsage` or `TypeName` in a new API,
34+
/// consider whether you need to define a type or refer to a type.
35+
///
36+
/// To define a type, use `TypeName`, and to refer to a type, use `TypeUsage`.
37+
///
38+
/// This type is not meant to represent all the various ways types can be
39+
/// wrapped in Swift, only the ways we wrap things in this project. For example,
40+
/// double optionals (`String??`) are automatically collapsed into a single
41+
/// optional, and so on.
42+
struct TypeUsage {
43+
44+
/// Describes either a type name or a type usage.
45+
fileprivate indirect enum Wrapped {
46+
47+
/// A type name, used to define a type.
48+
case name(TypeName)
49+
50+
/// A type usage, used to refer to a type.
51+
case usage(TypeUsage)
52+
}
53+
54+
/// The underlying type.
55+
fileprivate var wrapped: Wrapped
56+
57+
/// Describes the usage of the wrapped type.
58+
fileprivate enum Usage {
59+
60+
/// An unchanged underlying type.
61+
///
62+
/// For example: `Wrapped` stays `Wrapped`.
63+
case identity
64+
65+
/// An optional wrapper for the underlying type.
66+
///
67+
/// For example: `Wrapped` becomes `Wrapped?`.
68+
case optional
69+
70+
/// An array wrapped for the underlying type.
71+
///
72+
/// For example: `Wrapped` becomes `[Wrapped]`.
73+
case array
74+
75+
/// A dictionary value wrapper for the underlying type.
76+
///
77+
/// For example: `Wrapped` becomes `[String: Wrapped]`.
78+
case dictionaryValue
79+
80+
/// A generic type wrapper for the underlying type.
81+
///
82+
/// For example, `Wrapped` becomes `Wrapper<Wrapped>`.
83+
case generic(wrapper: TypeName)
84+
}
85+
86+
/// The type usage applied to the underlying type.
87+
fileprivate var usage: Usage
88+
}
89+
90+
extension TypeUsage: CustomStringConvertible { var description: String { fullyQualifiedName } }
91+
92+
extension TypeUsage {
93+
94+
/// A Boolean value that indicates whether the type is optional.
95+
var isOptional: Bool {
96+
guard case .optional = usage else { return false }
97+
return true
98+
}
99+
100+
/// A string representation of the last component of the Swift type name.
101+
///
102+
/// For example: `Int`.
103+
var shortName: String {
104+
let component: String
105+
switch wrapped {
106+
case let .name(typeName): component = typeName.shortName
107+
case let .usage(usage): component = usage.shortName
108+
}
109+
return applied(to: component)
110+
}
111+
112+
/// A string representation of the fully qualified type name.
113+
///
114+
/// For example: `Swift.Int`.
115+
var fullyQualifiedName: String {
116+
let component: String
117+
switch wrapped {
118+
case let .name(typeName): component = typeName.fullyQualifiedName
119+
case let .usage(usage): component = usage.fullyQualifiedName
120+
}
121+
return applied(to: component)
122+
}
123+
124+
/// A string representation of the fully qualified Swift type name, with
125+
/// any optional wrapping removed.
126+
///
127+
/// For example: `Swift.Int`.
128+
var fullyQualifiedNonOptionalName: String { withOptional(false).fullyQualifiedName }
129+
130+
/// Returns a string representation of the type usage applied to the
131+
/// specified Swift path component.
132+
/// - Parameter component: A Swift path component.
133+
/// - Returns: A string representation of the specified Swift path component with the applied type usage.
134+
private func applied(to component: String) -> String {
135+
switch usage {
136+
case .identity: return component
137+
case .optional: return component + "?"
138+
case .array: return "[" + component + "]"
139+
case .dictionaryValue: return "[String: " + component + "]"
140+
case .generic(wrapper: let wrapper):
141+
return "\(wrapper.fullyQualifiedName)<" + component + ">"
142+
}
143+
}
144+
145+
/// The type name wrapped by the current type usage.
146+
var typeName: TypeName {
147+
switch wrapped {
148+
case .name(let typeName): return typeName
149+
case .usage(let typeUsage): return typeUsage.typeName
150+
}
151+
}
152+
153+
/// A type usage created by treating the current type usage as an optional
154+
/// type.
155+
var asOptional: Self {
156+
// Don't double wrap optionals
157+
guard !isOptional else { return self }
158+
return TypeUsage(wrapped: .usage(self), usage: .optional)
159+
}
160+
161+
/// A type usage created by removing the outer type usage wrapper.
162+
private var unwrappedOneLevel: Self {
163+
switch wrapped {
164+
case let .usage(usage): return usage
165+
case let .name(typeName): return typeName.asUsage
166+
}
167+
}
168+
169+
/// Returns a type usage created by adding or removing an optional wrapper,
170+
/// controlled by the specified parameter.
171+
/// - Parameter isOptional: If `true`, wraps the current type usage in
172+
/// an optional. If `false`, removes a potential optional wrapper from the
173+
/// top level.
174+
/// - Returns: A type usage with the adjusted optionality based on the `isOptional` parameter.
175+
func withOptional(_ isOptional: Bool) -> Self {
176+
if (isOptional && self.isOptional) || (!isOptional && !self.isOptional) { return self }
177+
guard isOptional else { return unwrappedOneLevel }
178+
return asOptional
179+
}
180+
181+
/// A type usage created by treating the current type usage as the element
182+
/// type of an array.
183+
/// - Returns: A type usage for the array.
184+
var asArray: Self { TypeUsage(wrapped: .usage(self), usage: .array) }
185+
186+
/// A type usage created by treating the current type usage as the value
187+
/// type of a dictionary.
188+
/// - Returns: A type usage for the dictionary.
189+
var asDictionaryValue: Self { TypeUsage(wrapped: .usage(self), usage: .dictionaryValue) }
190+
191+
/// A type usage created by wrapping the current type usage inside the
192+
/// wrapper type, where the wrapper type is generic over the current type.
193+
func asWrapped(in wrapper: TypeName) -> Self {
194+
TypeUsage(wrapped: .usage(self), usage: .generic(wrapper: wrapper))
195+
}
196+
}
197+
198+
extension TypeName {
199+
200+
/// A type usage that wraps the current type name without changing it.
201+
var asUsage: TypeUsage { TypeUsage(wrapped: .name(self), usage: .identity) }
202+
}
203+
204+
extension ExistingTypeDescription {
205+
206+
/// Creates a new type description from the provided type usage's wrapped
207+
/// value.
208+
/// - Parameter wrapped: The wrapped value.
209+
private init(_ wrapped: TypeUsage.Wrapped) {
210+
switch wrapped {
211+
case .name(let typeName): self = .init(typeName)
212+
case .usage(let typeUsage): self = .init(typeUsage)
213+
}
214+
}
215+
216+
/// Creates a new type description from the provided type name.
217+
/// - Parameter typeName: A type name.
218+
init(_ typeName: TypeName) { self = .member(typeName.components) }
219+
220+
/// Creates a new type description from the provided type usage.
221+
/// - Parameter typeUsage: A type usage.
222+
init(_ typeUsage: TypeUsage) {
223+
switch typeUsage.usage {
224+
case .generic(wrapper: let wrapper):
225+
self = .generic(wrapper: .init(wrapper), wrapped: .init(typeUsage.wrapped))
226+
case .optional: self = .optional(.init(typeUsage.wrapped))
227+
case .identity: self = .init(typeUsage.wrapped)
228+
case .array: self = .array(.init(typeUsage.wrapped))
229+
case .dictionaryValue: self = .dictionaryValue(.init(typeUsage.wrapped))
230+
}
231+
}
232+
}

0 commit comments

Comments
 (0)