From 31282f2a6f7bc7e6732f581ff0fbaca6388b26a9 Mon Sep 17 00:00:00 2001 From: George Barnett Date: Fri, 10 Jan 2025 08:37:20 +0000 Subject: [PATCH 1/2] Adopt new serializer protocols Motivation: The core package made a few changes allowing for transport to define an associated bag-of-bytes types so that they can avoid copying to and from `[UInt8]`. This also came with changes to the serialization protocols. Modifications: - Add a thin adapter type to bridge between gRPC and Protobuf contiguous bytes - Adopt new protocols Result: Builds again --- Sources/GRPCProtobuf/Coding.swift | 15 +++-- .../GRPCProtobuf/ContiguousBytesAdapter.swift | 64 +++++++++++++++++++ .../Errors/Generated/error-service.grpc.swift | 8 +-- .../ProtobufCodingTests.swift | 4 +- 4 files changed, 80 insertions(+), 11 deletions(-) create mode 100644 Sources/GRPCProtobuf/ContiguousBytesAdapter.swift diff --git a/Sources/GRPCProtobuf/Coding.swift b/Sources/GRPCProtobuf/Coding.swift index df2e10f..ec1c0c4 100644 --- a/Sources/GRPCProtobuf/Coding.swift +++ b/Sources/GRPCProtobuf/Coding.swift @@ -25,9 +25,11 @@ public struct ProtobufSerializer: GRPCCore.Messa /// /// - Parameter message: The message to serialize. /// - Returns: An array of serialized bytes representing the message. - public func serialize(_ message: Message) throws -> [UInt8] { + @inlinable + public func serialize(_ message: Message) throws -> Bytes { do { - return try message.serializedBytes() + let adapter = try message.serializedBytes() as ContiguousBytesAdapter + return adapter.bytes } catch let error { throw RPCError( code: .invalidArgument, @@ -46,14 +48,17 @@ public struct ProtobufDeserializer: GRPCCore.Mes /// /// - Parameter serializedMessageBytes: The array of bytes to deserialize. /// - Returns: The deserialized message. - public func deserialize(_ serializedMessageBytes: [UInt8]) throws -> Message { + @inlinable + public func deserialize( + _ serializedMessageBytes: Bytes + ) throws -> Message { do { - let message = try Message(serializedBytes: serializedMessageBytes) + let message = try Message(serializedBytes: ContiguousBytesAdapter(serializedMessageBytes)) return message } catch let error { throw RPCError( code: .invalidArgument, - message: "Can't deserialize to message of type \(Message.self)", + message: "Can't deserialize to message of type \(Message.self) ERR: \(error)", cause: error ) } diff --git a/Sources/GRPCProtobuf/ContiguousBytesAdapter.swift b/Sources/GRPCProtobuf/ContiguousBytesAdapter.swift new file mode 100644 index 0000000..ee6db77 --- /dev/null +++ b/Sources/GRPCProtobuf/ContiguousBytesAdapter.swift @@ -0,0 +1,64 @@ +/* + * Copyright 2025, 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. + */ + +public import GRPCCore // internal but @usableFromInline +public import SwiftProtobuf // internal but @usableFromInline + +/// Brides between `GRPCContiguousBytes` and `SwiftProtobufContiguousBytes` which have the same +/// requirements. +/// +/// This is necessary as `SwiftProtobufContiguousBytes` can't be the protocol in the gRPC API (as +/// it'd require a dependency on Protobuf in the core package), and `GRPCContiguousBytes` can't +/// refine `SwiftProtobufContiguousBytes` for the same reason. +@usableFromInline +struct ContiguousBytesAdapter< + Bytes: GRPCContiguousBytes +>: GRPCContiguousBytes, SwiftProtobufContiguousBytes { + @usableFromInline + var bytes: Bytes + + @inlinable + init(_ bytes: Bytes) { + self.bytes = bytes + } + + @inlinable + init(repeating: UInt8, count: Int) { + self.bytes = Bytes(repeating: repeating, count: count) + } + + @inlinable + init(_ sequence: some Sequence) { + self.bytes = Bytes(sequence) + } + + @inlinable + var count: Int { + self.bytes.count + } + + @inlinable + func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { + try self.bytes.withUnsafeBytes(body) + } + + @inlinable + mutating func withUnsafeMutableBytes( + _ body: (UnsafeMutableRawBufferPointer) throws -> R + ) rethrows -> R { + try self.bytes.withUnsafeMutableBytes(body) + } +} diff --git a/Tests/GRPCProtobufTests/Errors/Generated/error-service.grpc.swift b/Tests/GRPCProtobufTests/Errors/Generated/error-service.grpc.swift index 66cc687..55a9269 100644 --- a/Tests/GRPCProtobufTests/Errors/Generated/error-service.grpc.swift +++ b/Tests/GRPCProtobufTests/Errors/Generated/error-service.grpc.swift @@ -134,7 +134,7 @@ extension ErrorService { // Default implementation of 'registerMethods(with:)'. extension ErrorService.StreamingServiceProtocol { - internal func registerMethods(with router: inout GRPCCore.RPCRouter) { + internal func registerMethods(with router: inout GRPCCore.RPCRouter) where Transport: GRPCCore.ServerTransport { router.registerHandler( forMethod: ErrorService.Method.ThrowError.descriptor, deserializer: GRPCProtobuf.ProtobufDeserializer(), @@ -212,14 +212,14 @@ extension ErrorService { /// The ``Client`` provides an implementation of ``ClientProtocol`` which wraps /// a `GRPCCore.GRPCCClient`. The underlying `GRPCClient` provides the long-lived /// means of communication with the remote peer. - internal struct Client: ClientProtocol { - private let client: GRPCCore.GRPCClient + internal struct Client: ClientProtocol where Transport: GRPCCore.ClientTransport { + private let client: GRPCCore.GRPCClient /// Creates a new client wrapping the provided `GRPCCore.GRPCClient`. /// /// - Parameters: /// - client: A `GRPCCore.GRPCClient` providing a communication channel to the service. - internal init(wrapping client: GRPCCore.GRPCClient) { + internal init(wrapping client: GRPCCore.GRPCClient) { self.client = client } diff --git a/Tests/GRPCProtobufTests/ProtobufCodingTests.swift b/Tests/GRPCProtobufTests/ProtobufCodingTests.swift index dd2202a..52c42a2 100644 --- a/Tests/GRPCProtobufTests/ProtobufCodingTests.swift +++ b/Tests/GRPCProtobufTests/ProtobufCodingTests.swift @@ -28,7 +28,7 @@ final class ProtobufCodingTests: XCTestCase { let serializer = ProtobufSerializer() let deserializer = ProtobufDeserializer() - let bytes = try serializer.serialize(message) + let bytes = try serializer.serialize(message) as [UInt8] let roundTrip = try deserializer.deserialize(bytes) XCTAssertEqual(roundTrip, message) } @@ -38,7 +38,7 @@ final class ProtobufCodingTests: XCTestCase { let serializer = ProtobufSerializer() XCTAssertThrowsError( - try serializer.serialize(message) + try serializer.serialize(message) as [UInt8] ) { error in XCTAssertEqual( error as? RPCError, From 76fcec46d25bd73832f8bbd849b1097b0d8dde6f Mon Sep 17 00:00:00 2001 From: George Barnett Date: Fri, 17 Jan 2025 11:15:29 +0000 Subject: [PATCH 2/2] fixup test --- Sources/GRPCProtobuf/Coding.swift | 2 +- .../ProtobufCodeGeneratorTests.swift | 8 ++++---- Tests/GRPCProtobufTests/ProtobufCodingTests.swift | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/GRPCProtobuf/Coding.swift b/Sources/GRPCProtobuf/Coding.swift index ec1c0c4..3160c08 100644 --- a/Sources/GRPCProtobuf/Coding.swift +++ b/Sources/GRPCProtobuf/Coding.swift @@ -58,7 +58,7 @@ public struct ProtobufDeserializer: GRPCCore.Mes } catch let error { throw RPCError( code: .invalidArgument, - message: "Can't deserialize to message of type \(Message.self) ERR: \(error)", + message: "Can't deserialize to message of type \(Message.self).", cause: error ) } diff --git a/Tests/GRPCProtobufCodeGenTests/ProtobufCodeGeneratorTests.swift b/Tests/GRPCProtobufCodeGenTests/ProtobufCodeGeneratorTests.swift index f9b3318..e3d3667 100644 --- a/Tests/GRPCProtobufCodeGenTests/ProtobufCodeGeneratorTests.swift +++ b/Tests/GRPCProtobufCodeGenTests/ProtobufCodeGeneratorTests.swift @@ -405,7 +405,7 @@ struct ProtobufCodeGeneratorTests { // Default implementation of 'registerMethods(with:)'. extension Test_TestService.StreamingServiceProtocol { - \(access) func registerMethods(with router: inout GRPCCore.RPCRouter) { + \(access) func registerMethods(with router: inout GRPCCore.RPCRouter) where Transport: GRPCCore.ServerTransport { router.registerHandler( forMethod: Test_TestService.Method.Unary.descriptor, deserializer: GRPCProtobuf.ProtobufDeserializer(), @@ -666,14 +666,14 @@ struct ProtobufCodeGeneratorTests { /// > Source IDL Documentation: /// > /// > Service docs. - \(access) struct Client: ClientProtocol { - private let client: GRPCCore.GRPCClient + \(access) struct Client: ClientProtocol where Transport: GRPCCore.ClientTransport { + private let client: GRPCCore.GRPCClient /// Creates a new client wrapping the provided `GRPCCore.GRPCClient`. /// /// - Parameters: /// - client: A `GRPCCore.GRPCClient` providing a communication channel to the service. - \(access) init(wrapping client: GRPCCore.GRPCClient) { + \(access) init(wrapping client: GRPCCore.GRPCClient) { self.client = client } diff --git a/Tests/GRPCProtobufTests/ProtobufCodingTests.swift b/Tests/GRPCProtobufTests/ProtobufCodingTests.swift index 52c42a2..6f30adf 100644 --- a/Tests/GRPCProtobufTests/ProtobufCodingTests.swift +++ b/Tests/GRPCProtobufTests/ProtobufCodingTests.swift @@ -65,7 +65,7 @@ final class ProtobufCodingTests: XCTestCase { code: .invalidArgument, message: """ - Can't deserialize to message of type TestMessage + Can't deserialize to message of type TestMessage. """ ) )