From c3a8716e5fff89349c0cd1eb6541457cf20557bb Mon Sep 17 00:00:00 2001 From: Michael Rebello Date: Mon, 13 Nov 2023 18:03:16 -0800 Subject: [PATCH] Reorganize project files & reduce public interface (#212) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reorganizes files in the project to be more clearly and consistently organized by visibility (whether the types within each file should be internal to the package or public). Also reduces the public interface of the module by moving some types to `internal` or `package`. Note that `package` is specific to SPM, so the keyword cannot be used in conjunction with CocoaPods. For this reason, a few of the types are guarded with `#if COCOAPODS`. It's also worth noting that `ConnectNIO` cannot be consumed via CocoaPods today because NIO does not support CocoaPods. For this reason, the shared gRPC interfaces between `Connect` and `ConnectNIO` don't need to be exposed to CocoaPods at all. Additionally, [`package` requires Swift 5.9](https://github.com/apple/swift-evolution/blob/main/proposals/0386-package-access-modifier.md), so the Swift package version has been updated as well. New directory structure: ``` . ├── Examples │   ├── ElizaCocoaPodsApp │   ├── ElizaSharedSources │   │   ├── AppSources │   │   └── GeneratedSources │   │   └── connectrpc │   │   └── eliza │   │   └── v1 │   └── ElizaSwiftPackageApp │   └── ElizaSwiftPackageApp ├── Libraries │   ├── Connect │   │   ├── Internal │   │   │   ├── Generated │   │   │   │   └── grpc │   │   │   │   └── status │   │   │   │   └── v1 │   │   │   ├── Interceptors │   │   │   ├── Locks │   │   │   ├── Streaming │   │   │   └── Unary │   │   ├── PackageInternal │   │   ├── Public │   │   │   ├── Implementation │   │   │   │   ├── Clients │   │   │   │   ├── Codecs │   │   │   │   └── Compression │   │   │   └── Interfaces │   │   │   ├── Interceptors │   │   │   └── Streaming │   │   │   ├── AsyncAwait │   │   │   └── Callbacks │   │   └── proto │   │   └── grpc │   │   └── status │   │   └── v1 │   ├── ConnectMocks │   └── ConnectNIO │   ├── Internal │   │   └── Extensions │   └── Public ├── Plugins │   ├── ConnectMocksPlugin │   ├── ConnectPluginUtilities │   └── ConnectSwiftPlugin └── Tests ├── ConnectLibraryTests │   ├── ConnectConformance │   ├── ConnectMocksTests │   ├── ConnectTests │   ├── Generated │   │   ├── connectrpc │   │   │   └── conformance │   │   │   └── v1 │   │   └── server │   │   └── v1 │   └── TestResources └── ConnectPluginUtilitiesTests ``` --- .github/workflows/ci.yaml | 3 + .swiftlint.yml | 2 +- .../Generated/grpc/status/v1/status.pb.swift | 0 .../Interceptors/ConnectInterceptor.swift | 0 .../Interceptors/GRPCWebInterceptor.swift | 0 .../Interceptors/InterceptorChain.swift | 0 .../Locks}/Lock.swift | 0 .../Locks}/Locked.swift | 8 +-- .../Streaming/BidirectionalAsyncStream.swift | 0 .../Streaming/BidirectionalStream.swift | 0 .../Streaming}/ConnectEndStreamResponse.swift | 0 .../Streaming/ServerOnlyAsyncStream.swift | 0 .../Streaming/ServerOnlyStream.swift | 0 .../Streaming/URLSessionStream.swift | 0 .../Unary}/UnaryAsyncWrapper.swift | 0 .../ConnectError+GRPC.swift | 12 +++- .../Codecs => PackageInternal}/Envelope.swift | 69 ++++++++++--------- .../Headers+GRPC.swift | 12 +++- .../Trailers+gRPC.swift} | 12 +++- .../Clients}/ProtocolClient.swift | 0 .../Clients}/URLSessionHTTPClient.swift | 0 .../Implementation/Codecs/JSONCodec.swift | 0 .../Implementation/Codecs/ProtoCodec.swift | 0 .../Compression/GzipCompressionPool.swift | 0 .../{ => Public}/Interfaces/Cancelable.swift | 0 .../{ => Public}/Interfaces/Code.swift | 0 .../{ => Public}/Interfaces/Codec.swift | 0 .../Interfaces/CompressionPool.swift | 0 .../Interfaces/ConnectError.swift | 0 .../Interfaces/HTTPClientInterface.swift | 0 .../{ => Public}/Interfaces/HTTPMethod.swift | 0 .../{ => Public}/Interfaces/HTTPMetrics.swift | 0 .../{ => Public}/Interfaces/HTTPRequest.swift | 0 .../Interfaces/HTTPResponse.swift | 0 .../Interfaces/HeaderConstants.swift | 0 .../{ => Public}/Interfaces/Headers.swift | 0 .../Interfaces/IdempotencyLevel.swift | 0 .../Interfaces/Interceptors/Interceptor.swift | 0 .../Interceptors/InterceptorFactory.swift | 0 .../Interceptors/StreamInterceptor.swift | 0 .../Interceptors/UnaryInterceptor.swift | 0 .../{ => Public}/Interfaces/MethodSpec.swift | 0 .../Interfaces/NetworkProtocol.swift | 0 .../Interfaces/ProtobufMessage.swift | 0 .../Interfaces}/ProtocolClientConfig.swift | 0 .../Interfaces/ProtocolClientInterface.swift | 0 .../Interfaces/ResponseMessage.swift | 0 .../BidirectionalAsyncStreamInterface.swift | 0 .../ClientOnlyAsyncStreamInterface.swift | 0 .../ServerOnlyAsyncStreamInterface.swift | 0 .../BidirectionalStreamInterface.swift | 0 .../ClientOnlyStreamInterface.swift | 0 .../Callbacks}/RequestCallbacks.swift | 0 .../Callbacks}/ResponseCallbacks.swift | 0 .../ServerOnlyStreamInterface.swift | 0 .../Interfaces/Streaming}/StreamResult.swift | 0 .../{ => Public}/Interfaces/Trailers.swift | 0 Libraries/Connect/buf.gen.yaml | 2 +- .../ConnectNIO/Internal/GRPCInterceptor.swift | 16 +++++ Makefile | 2 +- Package.swift | 15 +++- .../CallbackConformanceTests.swift | 2 +- .../ConnectMocksTests/ConnectMocksTests.swift | 2 +- .../ConnectTests/EnvelopeTests.swift | 2 +- 64 files changed, 112 insertions(+), 47 deletions(-) rename Libraries/Connect/{Implementation => Internal}/Generated/grpc/status/v1/status.pb.swift (100%) rename Libraries/Connect/{Implementation => Internal}/Interceptors/ConnectInterceptor.swift (100%) rename Libraries/Connect/{Implementation => Internal}/Interceptors/GRPCWebInterceptor.swift (100%) rename Libraries/Connect/{Implementation => Internal}/Interceptors/InterceptorChain.swift (100%) rename Libraries/Connect/{Implementation => Internal/Locks}/Lock.swift (100%) rename Libraries/Connect/{Implementation => Internal/Locks}/Locked.swift (88%) rename Libraries/Connect/{Implementation => Internal}/Streaming/BidirectionalAsyncStream.swift (100%) rename Libraries/Connect/{Implementation => Internal}/Streaming/BidirectionalStream.swift (100%) rename Libraries/Connect/{Interfaces => Internal/Streaming}/ConnectEndStreamResponse.swift (100%) rename Libraries/Connect/{Implementation => Internal}/Streaming/ServerOnlyAsyncStream.swift (100%) rename Libraries/Connect/{Implementation => Internal}/Streaming/ServerOnlyStream.swift (100%) rename Libraries/Connect/{Implementation => Internal}/Streaming/URLSessionStream.swift (100%) rename Libraries/Connect/{Implementation => Internal/Unary}/UnaryAsyncWrapper.swift (100%) rename Libraries/Connect/{Implementation/GRPC => PackageInternal}/ConnectError+GRPC.swift (89%) rename Libraries/Connect/{Implementation/Codecs => PackageInternal}/Envelope.swift (93%) rename Libraries/Connect/{Implementation/GRPC => PackageInternal}/Headers+GRPC.swift (79%) rename Libraries/Connect/{Implementation/GRPC/Trailers+GRPC.swift => PackageInternal/Trailers+gRPC.swift} (75%) rename Libraries/Connect/{Implementation => Public/Implementation/Clients}/ProtocolClient.swift (100%) rename Libraries/Connect/{Implementation => Public/Implementation/Clients}/URLSessionHTTPClient.swift (100%) rename Libraries/Connect/{ => Public}/Implementation/Codecs/JSONCodec.swift (100%) rename Libraries/Connect/{ => Public}/Implementation/Codecs/ProtoCodec.swift (100%) rename Libraries/Connect/{ => Public}/Implementation/Compression/GzipCompressionPool.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/Cancelable.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/Code.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/Codec.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/CompressionPool.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/ConnectError.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/HTTPClientInterface.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/HTTPMethod.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/HTTPMetrics.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/HTTPRequest.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/HTTPResponse.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/HeaderConstants.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/Headers.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/IdempotencyLevel.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/Interceptors/Interceptor.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/Interceptors/InterceptorFactory.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/Interceptors/StreamInterceptor.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/Interceptors/UnaryInterceptor.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/MethodSpec.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/NetworkProtocol.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/ProtobufMessage.swift (100%) rename Libraries/Connect/{Implementation => Public/Interfaces}/ProtocolClientConfig.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/ProtocolClientInterface.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/ResponseMessage.swift (100%) rename Libraries/Connect/{Interfaces/Streams => Public/Interfaces/Streaming/AsyncAwait}/BidirectionalAsyncStreamInterface.swift (100%) rename Libraries/Connect/{Interfaces/Streams => Public/Interfaces/Streaming/AsyncAwait}/ClientOnlyAsyncStreamInterface.swift (100%) rename Libraries/Connect/{Interfaces/Streams => Public/Interfaces/Streaming/AsyncAwait}/ServerOnlyAsyncStreamInterface.swift (100%) rename Libraries/Connect/{Interfaces/Streams => Public/Interfaces/Streaming/Callbacks}/BidirectionalStreamInterface.swift (100%) rename Libraries/Connect/{Interfaces/Streams => Public/Interfaces/Streaming/Callbacks}/ClientOnlyStreamInterface.swift (100%) rename Libraries/Connect/{Interfaces/Streams => Public/Interfaces/Streaming/Callbacks}/RequestCallbacks.swift (100%) rename Libraries/Connect/{Interfaces/Streams => Public/Interfaces/Streaming/Callbacks}/ResponseCallbacks.swift (100%) rename Libraries/Connect/{Interfaces/Streams => Public/Interfaces/Streaming/Callbacks}/ServerOnlyStreamInterface.swift (100%) rename Libraries/Connect/{Interfaces/Streams => Public/Interfaces/Streaming}/StreamResult.swift (100%) rename Libraries/Connect/{ => Public}/Interfaces/Trailers.swift (100%) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6fc4d8db..044b8c5f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -44,6 +44,9 @@ jobs: runs-on: macos-13 steps: - uses: actions/checkout@v4 + - name: Select Xcode version + # https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md#xcode + run: sudo xcode-select --switch /Applications/Xcode_15.0.app - uses: bufbuild/buf-setup-action@v1.28.0 with: github_token: ${{ github.token }} diff --git a/.swiftlint.yml b/.swiftlint.yml index b83c8f94..b4100727 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -4,7 +4,7 @@ included: - Plugins - Tests excluded: - - Libraries/Connect/Implementation/Generated + - Libraries/Connect/Internal/Generated - Tests/ConnectLibraryTests/Generated disabled_rules: - blanket_disable_command diff --git a/Libraries/Connect/Implementation/Generated/grpc/status/v1/status.pb.swift b/Libraries/Connect/Internal/Generated/grpc/status/v1/status.pb.swift similarity index 100% rename from Libraries/Connect/Implementation/Generated/grpc/status/v1/status.pb.swift rename to Libraries/Connect/Internal/Generated/grpc/status/v1/status.pb.swift diff --git a/Libraries/Connect/Implementation/Interceptors/ConnectInterceptor.swift b/Libraries/Connect/Internal/Interceptors/ConnectInterceptor.swift similarity index 100% rename from Libraries/Connect/Implementation/Interceptors/ConnectInterceptor.swift rename to Libraries/Connect/Internal/Interceptors/ConnectInterceptor.swift diff --git a/Libraries/Connect/Implementation/Interceptors/GRPCWebInterceptor.swift b/Libraries/Connect/Internal/Interceptors/GRPCWebInterceptor.swift similarity index 100% rename from Libraries/Connect/Implementation/Interceptors/GRPCWebInterceptor.swift rename to Libraries/Connect/Internal/Interceptors/GRPCWebInterceptor.swift diff --git a/Libraries/Connect/Implementation/Interceptors/InterceptorChain.swift b/Libraries/Connect/Internal/Interceptors/InterceptorChain.swift similarity index 100% rename from Libraries/Connect/Implementation/Interceptors/InterceptorChain.swift rename to Libraries/Connect/Internal/Interceptors/InterceptorChain.swift diff --git a/Libraries/Connect/Implementation/Lock.swift b/Libraries/Connect/Internal/Locks/Lock.swift similarity index 100% rename from Libraries/Connect/Implementation/Lock.swift rename to Libraries/Connect/Internal/Locks/Lock.swift diff --git a/Libraries/Connect/Implementation/Locked.swift b/Libraries/Connect/Internal/Locks/Locked.swift similarity index 88% rename from Libraries/Connect/Implementation/Locked.swift rename to Libraries/Connect/Internal/Locks/Locked.swift index 393a73c5..8219353a 100644 --- a/Libraries/Connect/Implementation/Locked.swift +++ b/Libraries/Connect/Internal/Locks/Locked.swift @@ -16,12 +16,12 @@ import Foundation /// Class containing an internal lock which can be used to ensure thread-safe access to an /// underlying value. Conforms to `Sendable`, making it accessible from `@Sendable` closures. -public final class Locked: @unchecked Sendable { +final class Locked: @unchecked Sendable { private let lock = Lock() private var wrappedValue: T /// Thread-safe access to the underlying value. - public var value: T { + var value: T { get { self.lock.perform { self.wrappedValue } } set { self.lock.perform { self.wrappedValue = newValue } } } @@ -29,13 +29,13 @@ public final class Locked: @unchecked Sendable { /// Perform an action with the underlying value, potentially updating that value. /// /// - parameter action: Closure to perform with the underlying value. - public func perform(action: @escaping (inout T) -> Void) { + func perform(action: @escaping (inout T) -> Void) { self.lock.perform { action(&self.wrappedValue) } } - public init(_ value: T) { + init(_ value: T) { self.wrappedValue = value } } diff --git a/Libraries/Connect/Implementation/Streaming/BidirectionalAsyncStream.swift b/Libraries/Connect/Internal/Streaming/BidirectionalAsyncStream.swift similarity index 100% rename from Libraries/Connect/Implementation/Streaming/BidirectionalAsyncStream.swift rename to Libraries/Connect/Internal/Streaming/BidirectionalAsyncStream.swift diff --git a/Libraries/Connect/Implementation/Streaming/BidirectionalStream.swift b/Libraries/Connect/Internal/Streaming/BidirectionalStream.swift similarity index 100% rename from Libraries/Connect/Implementation/Streaming/BidirectionalStream.swift rename to Libraries/Connect/Internal/Streaming/BidirectionalStream.swift diff --git a/Libraries/Connect/Interfaces/ConnectEndStreamResponse.swift b/Libraries/Connect/Internal/Streaming/ConnectEndStreamResponse.swift similarity index 100% rename from Libraries/Connect/Interfaces/ConnectEndStreamResponse.swift rename to Libraries/Connect/Internal/Streaming/ConnectEndStreamResponse.swift diff --git a/Libraries/Connect/Implementation/Streaming/ServerOnlyAsyncStream.swift b/Libraries/Connect/Internal/Streaming/ServerOnlyAsyncStream.swift similarity index 100% rename from Libraries/Connect/Implementation/Streaming/ServerOnlyAsyncStream.swift rename to Libraries/Connect/Internal/Streaming/ServerOnlyAsyncStream.swift diff --git a/Libraries/Connect/Implementation/Streaming/ServerOnlyStream.swift b/Libraries/Connect/Internal/Streaming/ServerOnlyStream.swift similarity index 100% rename from Libraries/Connect/Implementation/Streaming/ServerOnlyStream.swift rename to Libraries/Connect/Internal/Streaming/ServerOnlyStream.swift diff --git a/Libraries/Connect/Implementation/Streaming/URLSessionStream.swift b/Libraries/Connect/Internal/Streaming/URLSessionStream.swift similarity index 100% rename from Libraries/Connect/Implementation/Streaming/URLSessionStream.swift rename to Libraries/Connect/Internal/Streaming/URLSessionStream.swift diff --git a/Libraries/Connect/Implementation/UnaryAsyncWrapper.swift b/Libraries/Connect/Internal/Unary/UnaryAsyncWrapper.swift similarity index 100% rename from Libraries/Connect/Implementation/UnaryAsyncWrapper.swift rename to Libraries/Connect/Internal/Unary/UnaryAsyncWrapper.swift diff --git a/Libraries/Connect/Implementation/GRPC/ConnectError+GRPC.swift b/Libraries/Connect/PackageInternal/ConnectError+GRPC.swift similarity index 89% rename from Libraries/Connect/Implementation/GRPC/ConnectError+GRPC.swift rename to Libraries/Connect/PackageInternal/ConnectError+GRPC.swift index 7df94125..a233b5fa 100644 --- a/Libraries/Connect/Implementation/GRPC/ConnectError+GRPC.swift +++ b/Libraries/Connect/PackageInternal/ConnectError+GRPC.swift @@ -21,7 +21,17 @@ extension ConnectError { /// - parameter code: The status code received from the server. /// /// - returns: An error, if the status indicated an error. - public static func fromGRPCTrailers(_ trailers: Trailers, code: Code) -> Self? { +#if COCOAPODS // ConnectNIO is unavailable from CocoaPods, so this can be internal. + static func fromGRPCTrailers(_ trailers: Trailers, code: Code) -> Self? { + return self._fromGRPCTrailers(trailers, code: code) + } +#else + package static func fromGRPCTrailers(_ trailers: Trailers, code: Code) -> Self? { + return self._fromGRPCTrailers(trailers, code: code) + } +#endif + + private static func _fromGRPCTrailers(_ trailers: Trailers, code: Code) -> Self? { if code == .ok { return nil } diff --git a/Libraries/Connect/Implementation/Codecs/Envelope.swift b/Libraries/Connect/PackageInternal/Envelope.swift similarity index 93% rename from Libraries/Connect/Implementation/Codecs/Envelope.swift rename to Libraries/Connect/PackageInternal/Envelope.swift index 952246ea..f222d4ad 100644 --- a/Libraries/Connect/Implementation/Codecs/Envelope.swift +++ b/Libraries/Connect/PackageInternal/Envelope.swift @@ -16,39 +16,10 @@ import Foundation import SwiftProtobuf /// Provides functionality for packing and unpacking (headers and length prefixed) messages. +/// +/// This API is not considered part of Connect's public interface and is subject to change. +/// TODO: Make this `package` instead of `public` if/when CocoaPods support is dropped. public enum Envelope { - public enum Error: Swift.Error { - case missingExpectedCompressionPool - } - - /// The total number of bytes that will prefix a message. - public static var prefixLength: Int { - return 5 // Header flags (1 byte) + message length (4 bytes) - } - - /// Computes the length of the message contained by a packed chunk of data. - /// A packed chunk in this context refers to prefixed message data. - /// - /// Compliant with Connect streams: https://connectrpc.com/docs/protocol/#streaming-request - /// And gRPC: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#responses - /// - /// - parameter data: The packed data from which to determine the enveloped message's size. - /// - /// - returns: The length of the next expected message in the packed data. If multiple chunks - /// are specified, this will return the length of the first. Returns -1 if there is - /// not enough prefix data to determine the message length. - public static func messageLength(forPackedData data: Data) -> Int { - guard data.count >= self.prefixLength else { - return -1 - } - - // Skip header flags (1 byte) and determine the message length (next 4 bytes, big-endian) - var messageLength: UInt32 = 0 - (data[1...4] as NSData).getBytes(&messageLength, length: 4) - messageLength = UInt32(bigEndian: messageLength) - return Int(messageLength) - } - /// Packs a message into an "envelope", adding required header bytes and optionally /// applying compression. /// @@ -112,6 +83,40 @@ public enum Envelope { } } + // MARK: - Internal + + enum Error: Swift.Error { + case missingExpectedCompressionPool + } + + /// The total number of bytes that will prefix a message. + static var prefixLength: Int { + return 5 // Header flags (1 byte) + message length (4 bytes) + } + + /// Computes the length of the message contained by a packed chunk of data. + /// A packed chunk in this context refers to prefixed message data. + /// + /// Compliant with Connect streams: https://connectrpc.com/docs/protocol/#streaming-request + /// And gRPC: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#responses + /// + /// - parameter data: The packed data from which to determine the enveloped message's size. + /// + /// - returns: The length of the next expected message in the packed data. If multiple chunks + /// are specified, this will return the length of the first. Returns -1 if there is + /// not enough prefix data to determine the message length. + static func messageLength(forPackedData data: Data) -> Int { + guard data.count >= self.prefixLength else { + return -1 + } + + // Skip header flags (1 byte) and determine the message length (next 4 bytes, big-endian) + var messageLength: UInt32 = 0 + (data[1...4] as NSData).getBytes(&messageLength, length: 4) + messageLength = UInt32(bigEndian: messageLength) + return Int(messageLength) + } + // MARK: - Private private static func write(lengthOf message: Data, to buffer: inout Data) { diff --git a/Libraries/Connect/Implementation/GRPC/Headers+GRPC.swift b/Libraries/Connect/PackageInternal/Headers+GRPC.swift similarity index 79% rename from Libraries/Connect/Implementation/GRPC/Headers+GRPC.swift rename to Libraries/Connect/PackageInternal/Headers+GRPC.swift index 4ad0424e..fbf0d8ec 100644 --- a/Libraries/Connect/Implementation/GRPC/Headers+GRPC.swift +++ b/Libraries/Connect/PackageInternal/Headers+GRPC.swift @@ -22,7 +22,17 @@ extension Headers { /// - parameter grpcWeb: Should be true if using gRPC-Web, false if gRPC. /// /// - returns: A set of updated headers. - public func addingGRPCHeaders(using config: ProtocolClientConfig, grpcWeb: Bool) -> Self { +#if COCOAPODS // ConnectNIO is unavailable from CocoaPods, so this can be internal. + func addingGRPCHeaders(using config: ProtocolClientConfig, grpcWeb: Bool) -> Self { + return self._addingGRPCHeaders(using: config, grpcWeb: grpcWeb) + } +#else + package func addingGRPCHeaders(using config: ProtocolClientConfig, grpcWeb: Bool) -> Self { + return self._addingGRPCHeaders(using: config, grpcWeb: grpcWeb) + } +#endif + + private func _addingGRPCHeaders(using config: ProtocolClientConfig, grpcWeb: Bool) -> Self { var headers = self headers[HeaderConstants.grpcAcceptEncoding] = config .acceptCompressionPoolNames() diff --git a/Libraries/Connect/Implementation/GRPC/Trailers+GRPC.swift b/Libraries/Connect/PackageInternal/Trailers+gRPC.swift similarity index 75% rename from Libraries/Connect/Implementation/GRPC/Trailers+GRPC.swift rename to Libraries/Connect/PackageInternal/Trailers+gRPC.swift index 616ce71d..de718861 100644 --- a/Libraries/Connect/Implementation/GRPC/Trailers+GRPC.swift +++ b/Libraries/Connect/PackageInternal/Trailers+gRPC.swift @@ -16,7 +16,17 @@ extension Trailers { /// Identifies the status code from gRPC and gRPC-Web trailers. /// /// - returns: The gRPC status code, if specified. - public func grpcStatus() -> Code? { +#if COCOAPODS // ConnectNIO is unavailable from CocoaPods, so this can be internal. + func grpcStatus() -> Code? { + return self._grpcStatus() + } +#else + package func grpcStatus() -> Code? { + return self._grpcStatus() + } +#endif + + private func _grpcStatus() -> Code? { return self[HeaderConstants.grpcStatus]? .first .flatMap(Int.init) diff --git a/Libraries/Connect/Implementation/ProtocolClient.swift b/Libraries/Connect/Public/Implementation/Clients/ProtocolClient.swift similarity index 100% rename from Libraries/Connect/Implementation/ProtocolClient.swift rename to Libraries/Connect/Public/Implementation/Clients/ProtocolClient.swift diff --git a/Libraries/Connect/Implementation/URLSessionHTTPClient.swift b/Libraries/Connect/Public/Implementation/Clients/URLSessionHTTPClient.swift similarity index 100% rename from Libraries/Connect/Implementation/URLSessionHTTPClient.swift rename to Libraries/Connect/Public/Implementation/Clients/URLSessionHTTPClient.swift diff --git a/Libraries/Connect/Implementation/Codecs/JSONCodec.swift b/Libraries/Connect/Public/Implementation/Codecs/JSONCodec.swift similarity index 100% rename from Libraries/Connect/Implementation/Codecs/JSONCodec.swift rename to Libraries/Connect/Public/Implementation/Codecs/JSONCodec.swift diff --git a/Libraries/Connect/Implementation/Codecs/ProtoCodec.swift b/Libraries/Connect/Public/Implementation/Codecs/ProtoCodec.swift similarity index 100% rename from Libraries/Connect/Implementation/Codecs/ProtoCodec.swift rename to Libraries/Connect/Public/Implementation/Codecs/ProtoCodec.swift diff --git a/Libraries/Connect/Implementation/Compression/GzipCompressionPool.swift b/Libraries/Connect/Public/Implementation/Compression/GzipCompressionPool.swift similarity index 100% rename from Libraries/Connect/Implementation/Compression/GzipCompressionPool.swift rename to Libraries/Connect/Public/Implementation/Compression/GzipCompressionPool.swift diff --git a/Libraries/Connect/Interfaces/Cancelable.swift b/Libraries/Connect/Public/Interfaces/Cancelable.swift similarity index 100% rename from Libraries/Connect/Interfaces/Cancelable.swift rename to Libraries/Connect/Public/Interfaces/Cancelable.swift diff --git a/Libraries/Connect/Interfaces/Code.swift b/Libraries/Connect/Public/Interfaces/Code.swift similarity index 100% rename from Libraries/Connect/Interfaces/Code.swift rename to Libraries/Connect/Public/Interfaces/Code.swift diff --git a/Libraries/Connect/Interfaces/Codec.swift b/Libraries/Connect/Public/Interfaces/Codec.swift similarity index 100% rename from Libraries/Connect/Interfaces/Codec.swift rename to Libraries/Connect/Public/Interfaces/Codec.swift diff --git a/Libraries/Connect/Interfaces/CompressionPool.swift b/Libraries/Connect/Public/Interfaces/CompressionPool.swift similarity index 100% rename from Libraries/Connect/Interfaces/CompressionPool.swift rename to Libraries/Connect/Public/Interfaces/CompressionPool.swift diff --git a/Libraries/Connect/Interfaces/ConnectError.swift b/Libraries/Connect/Public/Interfaces/ConnectError.swift similarity index 100% rename from Libraries/Connect/Interfaces/ConnectError.swift rename to Libraries/Connect/Public/Interfaces/ConnectError.swift diff --git a/Libraries/Connect/Interfaces/HTTPClientInterface.swift b/Libraries/Connect/Public/Interfaces/HTTPClientInterface.swift similarity index 100% rename from Libraries/Connect/Interfaces/HTTPClientInterface.swift rename to Libraries/Connect/Public/Interfaces/HTTPClientInterface.swift diff --git a/Libraries/Connect/Interfaces/HTTPMethod.swift b/Libraries/Connect/Public/Interfaces/HTTPMethod.swift similarity index 100% rename from Libraries/Connect/Interfaces/HTTPMethod.swift rename to Libraries/Connect/Public/Interfaces/HTTPMethod.swift diff --git a/Libraries/Connect/Interfaces/HTTPMetrics.swift b/Libraries/Connect/Public/Interfaces/HTTPMetrics.swift similarity index 100% rename from Libraries/Connect/Interfaces/HTTPMetrics.swift rename to Libraries/Connect/Public/Interfaces/HTTPMetrics.swift diff --git a/Libraries/Connect/Interfaces/HTTPRequest.swift b/Libraries/Connect/Public/Interfaces/HTTPRequest.swift similarity index 100% rename from Libraries/Connect/Interfaces/HTTPRequest.swift rename to Libraries/Connect/Public/Interfaces/HTTPRequest.swift diff --git a/Libraries/Connect/Interfaces/HTTPResponse.swift b/Libraries/Connect/Public/Interfaces/HTTPResponse.swift similarity index 100% rename from Libraries/Connect/Interfaces/HTTPResponse.swift rename to Libraries/Connect/Public/Interfaces/HTTPResponse.swift diff --git a/Libraries/Connect/Interfaces/HeaderConstants.swift b/Libraries/Connect/Public/Interfaces/HeaderConstants.swift similarity index 100% rename from Libraries/Connect/Interfaces/HeaderConstants.swift rename to Libraries/Connect/Public/Interfaces/HeaderConstants.swift diff --git a/Libraries/Connect/Interfaces/Headers.swift b/Libraries/Connect/Public/Interfaces/Headers.swift similarity index 100% rename from Libraries/Connect/Interfaces/Headers.swift rename to Libraries/Connect/Public/Interfaces/Headers.swift diff --git a/Libraries/Connect/Interfaces/IdempotencyLevel.swift b/Libraries/Connect/Public/Interfaces/IdempotencyLevel.swift similarity index 100% rename from Libraries/Connect/Interfaces/IdempotencyLevel.swift rename to Libraries/Connect/Public/Interfaces/IdempotencyLevel.swift diff --git a/Libraries/Connect/Interfaces/Interceptors/Interceptor.swift b/Libraries/Connect/Public/Interfaces/Interceptors/Interceptor.swift similarity index 100% rename from Libraries/Connect/Interfaces/Interceptors/Interceptor.swift rename to Libraries/Connect/Public/Interfaces/Interceptors/Interceptor.swift diff --git a/Libraries/Connect/Interfaces/Interceptors/InterceptorFactory.swift b/Libraries/Connect/Public/Interfaces/Interceptors/InterceptorFactory.swift similarity index 100% rename from Libraries/Connect/Interfaces/Interceptors/InterceptorFactory.swift rename to Libraries/Connect/Public/Interfaces/Interceptors/InterceptorFactory.swift diff --git a/Libraries/Connect/Interfaces/Interceptors/StreamInterceptor.swift b/Libraries/Connect/Public/Interfaces/Interceptors/StreamInterceptor.swift similarity index 100% rename from Libraries/Connect/Interfaces/Interceptors/StreamInterceptor.swift rename to Libraries/Connect/Public/Interfaces/Interceptors/StreamInterceptor.swift diff --git a/Libraries/Connect/Interfaces/Interceptors/UnaryInterceptor.swift b/Libraries/Connect/Public/Interfaces/Interceptors/UnaryInterceptor.swift similarity index 100% rename from Libraries/Connect/Interfaces/Interceptors/UnaryInterceptor.swift rename to Libraries/Connect/Public/Interfaces/Interceptors/UnaryInterceptor.swift diff --git a/Libraries/Connect/Interfaces/MethodSpec.swift b/Libraries/Connect/Public/Interfaces/MethodSpec.swift similarity index 100% rename from Libraries/Connect/Interfaces/MethodSpec.swift rename to Libraries/Connect/Public/Interfaces/MethodSpec.swift diff --git a/Libraries/Connect/Interfaces/NetworkProtocol.swift b/Libraries/Connect/Public/Interfaces/NetworkProtocol.swift similarity index 100% rename from Libraries/Connect/Interfaces/NetworkProtocol.swift rename to Libraries/Connect/Public/Interfaces/NetworkProtocol.swift diff --git a/Libraries/Connect/Interfaces/ProtobufMessage.swift b/Libraries/Connect/Public/Interfaces/ProtobufMessage.swift similarity index 100% rename from Libraries/Connect/Interfaces/ProtobufMessage.swift rename to Libraries/Connect/Public/Interfaces/ProtobufMessage.swift diff --git a/Libraries/Connect/Implementation/ProtocolClientConfig.swift b/Libraries/Connect/Public/Interfaces/ProtocolClientConfig.swift similarity index 100% rename from Libraries/Connect/Implementation/ProtocolClientConfig.swift rename to Libraries/Connect/Public/Interfaces/ProtocolClientConfig.swift diff --git a/Libraries/Connect/Interfaces/ProtocolClientInterface.swift b/Libraries/Connect/Public/Interfaces/ProtocolClientInterface.swift similarity index 100% rename from Libraries/Connect/Interfaces/ProtocolClientInterface.swift rename to Libraries/Connect/Public/Interfaces/ProtocolClientInterface.swift diff --git a/Libraries/Connect/Interfaces/ResponseMessage.swift b/Libraries/Connect/Public/Interfaces/ResponseMessage.swift similarity index 100% rename from Libraries/Connect/Interfaces/ResponseMessage.swift rename to Libraries/Connect/Public/Interfaces/ResponseMessage.swift diff --git a/Libraries/Connect/Interfaces/Streams/BidirectionalAsyncStreamInterface.swift b/Libraries/Connect/Public/Interfaces/Streaming/AsyncAwait/BidirectionalAsyncStreamInterface.swift similarity index 100% rename from Libraries/Connect/Interfaces/Streams/BidirectionalAsyncStreamInterface.swift rename to Libraries/Connect/Public/Interfaces/Streaming/AsyncAwait/BidirectionalAsyncStreamInterface.swift diff --git a/Libraries/Connect/Interfaces/Streams/ClientOnlyAsyncStreamInterface.swift b/Libraries/Connect/Public/Interfaces/Streaming/AsyncAwait/ClientOnlyAsyncStreamInterface.swift similarity index 100% rename from Libraries/Connect/Interfaces/Streams/ClientOnlyAsyncStreamInterface.swift rename to Libraries/Connect/Public/Interfaces/Streaming/AsyncAwait/ClientOnlyAsyncStreamInterface.swift diff --git a/Libraries/Connect/Interfaces/Streams/ServerOnlyAsyncStreamInterface.swift b/Libraries/Connect/Public/Interfaces/Streaming/AsyncAwait/ServerOnlyAsyncStreamInterface.swift similarity index 100% rename from Libraries/Connect/Interfaces/Streams/ServerOnlyAsyncStreamInterface.swift rename to Libraries/Connect/Public/Interfaces/Streaming/AsyncAwait/ServerOnlyAsyncStreamInterface.swift diff --git a/Libraries/Connect/Interfaces/Streams/BidirectionalStreamInterface.swift b/Libraries/Connect/Public/Interfaces/Streaming/Callbacks/BidirectionalStreamInterface.swift similarity index 100% rename from Libraries/Connect/Interfaces/Streams/BidirectionalStreamInterface.swift rename to Libraries/Connect/Public/Interfaces/Streaming/Callbacks/BidirectionalStreamInterface.swift diff --git a/Libraries/Connect/Interfaces/Streams/ClientOnlyStreamInterface.swift b/Libraries/Connect/Public/Interfaces/Streaming/Callbacks/ClientOnlyStreamInterface.swift similarity index 100% rename from Libraries/Connect/Interfaces/Streams/ClientOnlyStreamInterface.swift rename to Libraries/Connect/Public/Interfaces/Streaming/Callbacks/ClientOnlyStreamInterface.swift diff --git a/Libraries/Connect/Interfaces/Streams/RequestCallbacks.swift b/Libraries/Connect/Public/Interfaces/Streaming/Callbacks/RequestCallbacks.swift similarity index 100% rename from Libraries/Connect/Interfaces/Streams/RequestCallbacks.swift rename to Libraries/Connect/Public/Interfaces/Streaming/Callbacks/RequestCallbacks.swift diff --git a/Libraries/Connect/Interfaces/Streams/ResponseCallbacks.swift b/Libraries/Connect/Public/Interfaces/Streaming/Callbacks/ResponseCallbacks.swift similarity index 100% rename from Libraries/Connect/Interfaces/Streams/ResponseCallbacks.swift rename to Libraries/Connect/Public/Interfaces/Streaming/Callbacks/ResponseCallbacks.swift diff --git a/Libraries/Connect/Interfaces/Streams/ServerOnlyStreamInterface.swift b/Libraries/Connect/Public/Interfaces/Streaming/Callbacks/ServerOnlyStreamInterface.swift similarity index 100% rename from Libraries/Connect/Interfaces/Streams/ServerOnlyStreamInterface.swift rename to Libraries/Connect/Public/Interfaces/Streaming/Callbacks/ServerOnlyStreamInterface.swift diff --git a/Libraries/Connect/Interfaces/Streams/StreamResult.swift b/Libraries/Connect/Public/Interfaces/Streaming/StreamResult.swift similarity index 100% rename from Libraries/Connect/Interfaces/Streams/StreamResult.swift rename to Libraries/Connect/Public/Interfaces/Streaming/StreamResult.swift diff --git a/Libraries/Connect/Interfaces/Trailers.swift b/Libraries/Connect/Public/Interfaces/Trailers.swift similarity index 100% rename from Libraries/Connect/Interfaces/Trailers.swift rename to Libraries/Connect/Public/Interfaces/Trailers.swift diff --git a/Libraries/Connect/buf.gen.yaml b/Libraries/Connect/buf.gen.yaml index d6a057bc..4c79ffda 100644 --- a/Libraries/Connect/buf.gen.yaml +++ b/Libraries/Connect/buf.gen.yaml @@ -2,4 +2,4 @@ version: v1 plugins: - plugin: buf.build/apple/swift:v1.25.1 opt: Visibility=Internal - out: ./Implementation/Generated + out: ./Internal/Generated diff --git a/Libraries/ConnectNIO/Internal/GRPCInterceptor.swift b/Libraries/ConnectNIO/Internal/GRPCInterceptor.swift index 9fd05302..006f8839 100644 --- a/Libraries/ConnectNIO/Internal/GRPCInterceptor.swift +++ b/Libraries/ConnectNIO/Internal/GRPCInterceptor.swift @@ -14,6 +14,7 @@ import Connect import Foundation +import NIOConcurrencyHelpers /// Implementation of the gRPC protocol as an interceptor. /// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md @@ -186,3 +187,18 @@ extension GRPCInterceptor: StreamInterceptor { } } } + +private final class Locked: @unchecked Sendable { + private let lock = NIOLock() + private var wrappedValue: T + + /// Thread-safe access to the underlying value. + var value: T { + get { self.lock.withLock { self.wrappedValue } } + set { self.lock.withLock { self.wrappedValue = newValue } } + } + + init(_ value: T) { + self.wrappedValue = value + } +} diff --git a/Makefile b/Makefile index eed2f1c2..64e04f78 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ LICENSE_HEADER_VERSION := v1.12.0 LICENSE_IGNORE := -e Package.swift \ -e $(BIN)\/ \ -e Examples/ElizaSharedSources/GeneratedSources\/ \ - -e Libraries/Connect/Implementation/Generated\/ \ + -e Libraries/Connect/Internal/Generated\/ \ -e Tests/ConnectLibraryTests/proto/grpc\/ \ -e Tests/ConnectLibraryTests/Generated\/ diff --git a/Package.swift b/Package.swift index 092d450a..c88f6ecb 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.6 +// swift-tools-version:5.9 // Copyright 2022-2023 Buf Technologies, Inc. // @@ -16,8 +16,10 @@ import PackageDescription +private let packageName = "Connect" + let package = Package( - name: "Connect", + name: packageName, platforms: [ .iOS(.v12), .macOS(.v10_15), @@ -75,6 +77,9 @@ let package = Package( "buf.work.yaml", "proto", "README.md", + ], + swiftSettings: [ + .unsafeFlags(["-package-name", packageName]) ] ), .testTarget( @@ -104,6 +109,9 @@ let package = Package( path: "Libraries/ConnectMocks", exclude: [ "README.md", + ], + swiftSettings: [ + .unsafeFlags(["-package-name", packageName]) ] ), .executableTarget( @@ -129,6 +137,9 @@ let package = Package( path: "Libraries/ConnectNIO", exclude: [ "README.md", + ], + swiftSettings: [ + .unsafeFlags(["-package-name", packageName]) ] ), .target( diff --git a/Tests/ConnectLibraryTests/ConnectConformance/CallbackConformanceTests.swift b/Tests/ConnectLibraryTests/ConnectConformance/CallbackConformanceTests.swift index 1f84d521..2900fd49 100644 --- a/Tests/ConnectLibraryTests/ConnectConformance/CallbackConformanceTests.swift +++ b/Tests/ConnectLibraryTests/ConnectConformance/CallbackConformanceTests.swift @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import Connect +@testable import Connect import Foundation import SwiftProtobuf import XCTest diff --git a/Tests/ConnectLibraryTests/ConnectMocksTests/ConnectMocksTests.swift b/Tests/ConnectLibraryTests/ConnectMocksTests/ConnectMocksTests.swift index 30c30028..2962c95f 100644 --- a/Tests/ConnectLibraryTests/ConnectMocksTests/ConnectMocksTests.swift +++ b/Tests/ConnectLibraryTests/ConnectMocksTests/ConnectMocksTests.swift @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import Connect +@testable import Connect import ConnectMocks import SwiftProtobuf import XCTest diff --git a/Tests/ConnectLibraryTests/ConnectTests/EnvelopeTests.swift b/Tests/ConnectLibraryTests/ConnectTests/EnvelopeTests.swift index be693d09..b0a784d0 100644 --- a/Tests/ConnectLibraryTests/ConnectTests/EnvelopeTests.swift +++ b/Tests/ConnectLibraryTests/ConnectTests/EnvelopeTests.swift @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import Connect +@testable import Connect import XCTest final class EnvelopeTests: XCTestCase {