Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion NOTICES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,11 @@ This product uses derivations of swift-extras/swift-extras-base64 'Base64.swift'

---

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

* LICENSE (Apache License 2.0):
* https://github.com/apple/swift-openapi-generator/blob/main/LICENSE.txt
* HOMEPAGE:
* https://github.com/apple/swift-openapi-generator

105 changes: 105 additions & 0 deletions Sources/GRPCCodeGen/Internal/TypeName.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright 2023, gRPC Authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//===----------------------------------------------------------------------===//
//
// 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

/// A fully-qualified type name that contains the components of the Swift
/// type name.
///
/// Use the type name to define a type, see also `TypeUsage` when referring
/// to a type.
struct TypeName: Hashable {
/// A list of components that make up the type name.
private let components: [String]

/// Creates a new type name with the specified list of components.
/// - Parameter components: A list of components for the type.
init(components: [String]) {
precondition(!components.isEmpty, "TypeName path cannot be empty")
self.components = components
}

/// A string representation of the fully qualified Swift type name.
///
/// For example: `Swift.Int`.
var fullyQualifiedName: String { components.joined(separator: ".") }

/// A string representation of the last path component of the Swift
/// type name.
///
/// For example: `Int`.
var shortName: String { components.last! }

/// Returns a type name by appending the specified component to the
/// current type name.
///
/// In other words, returns a type name for a child type.
/// - Parameters:
/// - component: The name of the Swift type component.
/// - Returns: A new type name.
func appending(component: String) -> Self {
return .init(components: components + [component])
}

/// Returns a type name by removing the last component from the current
/// type name.
///
/// In other words, returns a type name for the parent type.
var parent: TypeName {
precondition(components.count >= 1, "Cannot get the parent of a root type")
return .init(components: components.dropLast())
}
}

extension TypeName: CustomStringConvertible {
var description: String {
return fullyQualifiedSwiftName
}
}

extension TypeName {
/// Returns the type name for the String type.
static var string: Self { .swift("String") }

/// Returns the type name for the Int type.
static var int: Self { .swift("Int") }

/// Returns a type name for a type with the specified name in the
/// Swift module.
/// - Parameter name: The name of the type.
/// - Returns: A TypeName representing the specified type within the Swift module.
static func swift(_ name: String) -> TypeName { TypeName(components: ["Swift", name]) }

/// Returns a type name for a type with the specified name in the
/// Foundation module.
/// - Parameter name: The name of the type.
/// - Returns: A TypeName representing the specified type within the Foundation module.
static func foundation(_ name: String) -> TypeName {
TypeName(components: ["Foundation", name])
}
}
232 changes: 232 additions & 0 deletions Sources/GRPCCodeGen/Internal/TypeUsage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
/*
* Copyright 2023, gRPC Authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

/// A reference to a Swift type, including modifiers such as whether the
/// type is wrapped in an optional, an array, or a dictionary.
///
/// Whenever unsure whether to use `TypeUsage` or `TypeName` in a new API,
/// consider whether you need to define a type or refer to a type.
///
/// To define a type, use `TypeName`, and to refer to a type, use `TypeUsage`.
///
/// This type is not meant to represent all the various ways types can be
/// wrapped in Swift, only the ways we wrap things in this project. For example,
/// double optionals (`String??`) are automatically collapsed into a single
/// optional, and so on.
struct TypeUsage {

/// Describes either a type name or a type usage.
fileprivate indirect enum Wrapped {

/// A type name, used to define a type.
case name(TypeName)

/// A type usage, used to refer to a type.
case usage(TypeUsage)
}

/// The underlying type.
fileprivate var wrapped: Wrapped

/// Describes the usage of the wrapped type.
fileprivate enum Usage {

/// An unchanged underlying type.
///
/// For example: `Wrapped` stays `Wrapped`.
case identity

/// An optional wrapper for the underlying type.
///
/// For example: `Wrapped` becomes `Wrapped?`.
case optional

/// An array wrapped for the underlying type.
///
/// For example: `Wrapped` becomes `[Wrapped]`.
case array

/// A dictionary value wrapper for the underlying type.
///
/// For example: `Wrapped` becomes `[String: Wrapped]`.
case dictionaryValue

/// A generic type wrapper for the underlying type.
///
/// For example, `Wrapped` becomes `Wrapper<Wrapped>`.
case generic(wrapper: TypeName)
}

/// The type usage applied to the underlying type.
fileprivate var usage: Usage
}

extension TypeUsage: CustomStringConvertible { var description: String { fullyQualifiedName } }

extension TypeUsage {

/// A Boolean value that indicates whether the type is optional.
var isOptional: Bool {
guard case .optional = usage else { return false }
return true
}

/// A string representation of the last component of the Swift type name.
///
/// For example: `Int`.
var shortName: String {
let component: String
switch wrapped {
case let .name(typeName): component = typeName.shortName
case let .usage(usage): component = usage.shortName
}
return applied(to: component)
}

/// A string representation of the fully qualified type name.
///
/// For example: `Swift.Int`.
var fullyQualifiedName: String {
let component: String
switch wrapped {
case let .name(typeName): component = typeName.fullyQualifiedName
case let .usage(usage): component = usage.fullyQualifiedName
}
return applied(to: component)
}

/// A string representation of the fully qualified Swift type name, with
/// any optional wrapping removed.
///
/// For example: `Swift.Int`.
var fullyQualifiedNonOptionalName: String { withOptional(false).fullyQualifiedName }

/// Returns a string representation of the type usage applied to the
/// specified Swift path component.
/// - Parameter component: A Swift path component.
/// - Returns: A string representation of the specified Swift path component with the applied type usage.
private func applied(to component: String) -> String {
switch usage {
case .identity: return component
case .optional: return component + "?"
case .array: return "[" + component + "]"
case .dictionaryValue: return "[String: " + component + "]"
case .generic(wrapper: let wrapper):
return "\(wrapper.fullyQualifiedName)<" + component + ">"
}
}

/// The type name wrapped by the current type usage.
var typeName: TypeName {
switch wrapped {
case .name(let typeName): return typeName
case .usage(let typeUsage): return typeUsage.typeName
}
}

/// A type usage created by treating the current type usage as an optional
/// type.
var asOptional: Self {
// Don't double wrap optionals
guard !isOptional else { return self }
return TypeUsage(wrapped: .usage(self), usage: .optional)
}

/// A type usage created by removing the outer type usage wrapper.
private var unwrappedOneLevel: Self {
switch wrapped {
case let .usage(usage): return usage
case let .name(typeName): return typeName.asUsage
}
}

/// Returns a type usage created by adding or removing an optional wrapper,
/// controlled by the specified parameter.
/// - Parameter isOptional: If `true`, wraps the current type usage in
/// an optional. If `false`, removes a potential optional wrapper from the
/// top level.
/// - Returns: A type usage with the adjusted optionality based on the `isOptional` parameter.
func withOptional(_ isOptional: Bool) -> Self {
if (isOptional && self.isOptional) || (!isOptional && !self.isOptional) { return self }
guard isOptional else { return unwrappedOneLevel }
return asOptional
}

/// A type usage created by treating the current type usage as the element
/// type of an array.
/// - Returns: A type usage for the array.
var asArray: Self { TypeUsage(wrapped: .usage(self), usage: .array) }

/// A type usage created by treating the current type usage as the value
/// type of a dictionary.
/// - Returns: A type usage for the dictionary.
var asDictionaryValue: Self { TypeUsage(wrapped: .usage(self), usage: .dictionaryValue) }

/// A type usage created by wrapping the current type usage inside the
/// wrapper type, where the wrapper type is generic over the current type.
func asWrapped(in wrapper: TypeName) -> Self {
TypeUsage(wrapped: .usage(self), usage: .generic(wrapper: wrapper))
}
}

extension TypeName {

/// A type usage that wraps the current type name without changing it.
var asUsage: TypeUsage { TypeUsage(wrapped: .name(self), usage: .identity) }
}

extension ExistingTypeDescription {

/// Creates a new type description from the provided type usage's wrapped
/// value.
/// - Parameter wrapped: The wrapped value.
private init(_ wrapped: TypeUsage.Wrapped) {
switch wrapped {
case .name(let typeName): self = .init(typeName)
case .usage(let typeUsage): self = .init(typeUsage)
}
}

/// Creates a new type description from the provided type name.
/// - Parameter typeName: A type name.
init(_ typeName: TypeName) { self = .member(typeName.components) }

/// Creates a new type description from the provided type usage.
/// - Parameter typeUsage: A type usage.
init(_ typeUsage: TypeUsage) {
switch typeUsage.usage {
case .generic(wrapper: let wrapper):
self = .generic(wrapper: .init(wrapper), wrapped: .init(typeUsage.wrapped))
case .optional: self = .optional(.init(typeUsage.wrapped))
case .identity: self = .init(typeUsage.wrapped)
case .array: self = .array(.init(typeUsage.wrapped))
case .dictionaryValue: self = .dictionaryValue(.init(typeUsage.wrapped))
}
}
}