diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3f1a04f..02e6a3e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,10 +13,10 @@ jobs: with: linux_5_9_enabled: false linux_5_10_enabled: false - linux_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -warnings-as-errors" - linux_6_1_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -warnings-as-errors" - linux_nightly_next_arguments_override: "--explicit-target-dependency-import-check error" - linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error" + linux_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -warnings-as-errors -Xswiftc -require-explicit-availability" + linux_6_1_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -warnings-as-errors -Xswiftc -require-explicit-availability" + linux_nightly_next_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-availability" + linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-availability" construct-plugin-tests-matrix: name: Construct plugin tests matrix diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index cdbf6de..9916556 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -25,10 +25,10 @@ jobs: with: linux_5_9_enabled: false linux_5_10_enabled: false - linux_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -warnings-as-errors" - linux_6_1_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -warnings-as-errors" - linux_nightly_next_arguments_override: "--explicit-target-dependency-import-check error" - linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error" + linux_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -warnings-as-errors -Xswiftc -require-explicit-availability" + linux_6_1_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -warnings-as-errors -Xswiftc -require-explicit-availability" + linux_nightly_next_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-availability" + linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-availability" construct-plugin-tests-matrix: name: Construct plugin tests matrix diff --git a/Package.swift b/Package.swift index 978748e..2337c52 100644 --- a/Package.swift +++ b/Package.swift @@ -39,7 +39,7 @@ let products: [Product] = [ let dependencies: [Package.Dependency] = [ .package( url: "https://github.com/grpc/grpc-swift.git", - from: "2.2.0" + from: "2.2.1" ), .package( url: "https://github.com/apple/swift-protobuf.git", @@ -47,12 +47,28 @@ let dependencies: [Package.Dependency] = [ ), ] -let defaultSwiftSettings: [SwiftSetting] = [ - .swiftLanguageMode(.v6), - .enableUpcomingFeature("ExistentialAny"), - .enableUpcomingFeature("InternalImportsByDefault"), - .enableUpcomingFeature("MemberImportVisibility"), -] +// ------------------------------------------------------------------------------------------------- + +// This adds some build settings which allow us to map "@available(gRPCSwiftProtobuf 1.x, *)" to +// the appropriate OS platforms. +let nextMinorVersion = 3 +let availabilitySettings: [SwiftSetting] = (0 ... nextMinorVersion).map { minor in + let name = "gRPCSwiftProtobuf" + let version = "1.\(minor)" + let platforms = "macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0" + let setting = "AvailabilityMacro=\(name) \(version):\(platforms)" + return .enableExperimentalFeature(setting) +} + +let defaultSwiftSettings: [SwiftSetting] = + availabilitySettings + [ + .swiftLanguageMode(.v6), + .enableUpcomingFeature("ExistentialAny"), + .enableUpcomingFeature("InternalImportsByDefault"), + .enableUpcomingFeature("MemberImportVisibility"), + ] + +// ------------------------------------------------------------------------------------------------- var targets: [Target] = [ // protoc plugin for grpc-swift @@ -190,13 +206,6 @@ if Context.buildCGRPCProtobuf { let package = Package( name: "grpc-swift-protobuf", - platforms: [ - .macOS(.v15), - .iOS(.v18), - .tvOS(.v18), - .watchOS(.v11), - .visionOS(.v2), - ], products: products, dependencies: dependencies, targets: targets diff --git a/Sources/GRPCProtobuf/Coding.swift b/Sources/GRPCProtobuf/Coding.swift index 3160c08..90d15ab 100644 --- a/Sources/GRPCProtobuf/Coding.swift +++ b/Sources/GRPCProtobuf/Coding.swift @@ -18,6 +18,7 @@ public import GRPCCore public import SwiftProtobuf /// Serializes a Protobuf message into a sequence of bytes. +@available(gRPCSwiftProtobuf 1.0, *) public struct ProtobufSerializer: GRPCCore.MessageSerializer { public init() {} @@ -41,6 +42,7 @@ public struct ProtobufSerializer: GRPCCore.Messa } /// Deserializes a sequence of bytes into a Protobuf message. +@available(gRPCSwiftProtobuf 1.0, *) public struct ProtobufDeserializer: GRPCCore.MessageDeserializer { public init() {} diff --git a/Sources/GRPCProtobuf/ContiguousBytesAdapter.swift b/Sources/GRPCProtobuf/ContiguousBytesAdapter.swift index ee6db77..54df2f5 100644 --- a/Sources/GRPCProtobuf/ContiguousBytesAdapter.swift +++ b/Sources/GRPCProtobuf/ContiguousBytesAdapter.swift @@ -24,6 +24,7 @@ public import SwiftProtobuf // internal but @usableFromInline /// it'd require a dependency on Protobuf in the core package), and `GRPCContiguousBytes` can't /// refine `SwiftProtobufContiguousBytes` for the same reason. @usableFromInline +@available(gRPCSwiftProtobuf 1.0, *) struct ContiguousBytesAdapter< Bytes: GRPCContiguousBytes >: GRPCContiguousBytes, SwiftProtobufContiguousBytes { diff --git a/Sources/GRPCProtobuf/Errors/ErrorDetails+AnyPacking.swift b/Sources/GRPCProtobuf/Errors/ErrorDetails+AnyPacking.swift index 1fe4ee5..d3bd45c 100644 --- a/Sources/GRPCProtobuf/Errors/ErrorDetails+AnyPacking.swift +++ b/Sources/GRPCProtobuf/Errors/ErrorDetails+AnyPacking.swift @@ -61,6 +61,7 @@ extension Google_Protobuf_Any { } } +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails { // Note: this type isn't packable into an 'Any' protobuf so doesn't conform // to 'GoogleProtobufAnyPackable' despite holding types which are packable. diff --git a/Sources/GRPCProtobuf/Errors/ErrorDetails+CustomStringConvertible.swift b/Sources/GRPCProtobuf/Errors/ErrorDetails+CustomStringConvertible.swift index c781aaa..daee37a 100644 --- a/Sources/GRPCProtobuf/Errors/ErrorDetails+CustomStringConvertible.swift +++ b/Sources/GRPCProtobuf/Errors/ErrorDetails+CustomStringConvertible.swift @@ -14,6 +14,7 @@ * limitations under the License. */ +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails: CustomStringConvertible { public var description: String { switch self.wrapped { @@ -46,54 +47,63 @@ extension ErrorDetails: CustomStringConvertible { // Some errors use protobuf messages as their storage so the default description isn't // representative +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails.ErrorInfo: CustomStringConvertible { public var description: String { "\(Self.self)(reason: \"\(self.reason)\", domain: \"\(self.domain)\", metadata: \(self.metadata))" } } +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails.DebugInfo: CustomStringConvertible { public var description: String { "\(Self.self)(stack: \(self.stack), detail: \"\(self.detail)\")" } } +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails.QuotaFailure.Violation: CustomStringConvertible { public var description: String { "\(Self.self)(subject: \"\(self.subject)\", violationDescription: \"\(self.violationDescription)\")" } } +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails.PreconditionFailure.Violation: CustomStringConvertible { public var description: String { "\(Self.self)(subject: \"\(self.subject)\", type: \"\(self.type)\", violationDescription: \"\(self.violationDescription)\")" } } +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails.BadRequest.FieldViolation: CustomStringConvertible { public var description: String { "\(Self.self)(field: \"\(self.field)\", violationDescription: \"\(self.violationDescription)\")" } } +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails.RequestInfo: CustomStringConvertible { public var description: String { "\(Self.self)(requestID: \"\(self.requestID)\", servingData: \"\(self.servingData)\")" } } +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails.ResourceInfo: CustomStringConvertible { public var description: String { "\(Self.self)(name: \"\(self.name)\", owner: \"\(self.owner)\", type: \"\(self.type)\", errorDescription: \"\(self.errorDescription)\")" } } +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails.Help.Link: CustomStringConvertible { public var description: String { "\(Self.self)(url: \"\(self.url)\", linkDescription: \"\(self.linkDescription)\")" } } +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails.LocalizedMessage: CustomStringConvertible { public var description: String { "\(Self.self)(locale: \"\(self.locale)\", message: \"\(self.message)\")" diff --git a/Sources/GRPCProtobuf/Errors/ErrorDetails+Types.swift b/Sources/GRPCProtobuf/Errors/ErrorDetails+Types.swift index 1a7f6b4..ba876d8 100644 --- a/Sources/GRPCProtobuf/Errors/ErrorDetails+Types.swift +++ b/Sources/GRPCProtobuf/Errors/ErrorDetails+Types.swift @@ -16,6 +16,7 @@ internal import SwiftProtobuf +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails { /// Describes the cause of the error with structured details. /// diff --git a/Sources/GRPCProtobuf/Errors/ErrorDetails.swift b/Sources/GRPCProtobuf/Errors/ErrorDetails.swift index 9619762..31bfcdb 100644 --- a/Sources/GRPCProtobuf/Errors/ErrorDetails.swift +++ b/Sources/GRPCProtobuf/Errors/ErrorDetails.swift @@ -24,6 +24,7 @@ public import SwiftProtobuf /// /// This type also allows you to provide wrap your own error details up as an "Any" /// protobuf (`Google_Protobuf_Any`). +@available(gRPCSwiftProtobuf 1.0, *) public struct ErrorDetails: Sendable, Hashable { enum Wrapped: Sendable, Hashable { case errorInfo(ErrorInfo) @@ -198,6 +199,7 @@ public struct ErrorDetails: Sendable, Hashable { } } +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails { /// Returns error info if set. public var errorInfo: ErrorInfo? { diff --git a/Sources/GRPCProtobuf/Errors/GoogleRPCStatus.swift b/Sources/GRPCProtobuf/Errors/GoogleRPCStatus.swift index 63ba327..6df2d46 100644 --- a/Sources/GRPCProtobuf/Errors/GoogleRPCStatus.swift +++ b/Sources/GRPCProtobuf/Errors/GoogleRPCStatus.swift @@ -35,6 +35,7 @@ public import SwiftProtobuf /// > inserted into the metadata keyed by "grpc-status-details-bin". The value of the metadata is /// > the serialized bytes of a "google.rpc.Status" protocol buffers message containing the status /// > code, message, and details. +@available(gRPCSwiftProtobuf 1.0, *) public struct GoogleRPCStatus: Error, Hashable { /// A code representing the high-level domain of the error. public var code: RPCError.Code @@ -73,6 +74,7 @@ public struct GoogleRPCStatus: Error, Hashable { } } +@available(gRPCSwiftProtobuf 1.0, *) extension GoogleRPCStatus { /// Creates a new message by decoding the given `SwiftProtobufContiguousBytes` value /// containing a serialized message in Protocol Buffer binary format. @@ -133,6 +135,7 @@ extension GoogleRPCStatus { } } +@available(gRPCSwiftProtobuf 1.0, *) extension GoogleRPCStatus: RPCErrorConvertible { public var rpcErrorCode: RPCError.Code { self.code } public var rpcErrorMessage: String { self.message } diff --git a/Sources/GRPCProtobuf/Errors/RPCError+GoogleRPCStatus.swift b/Sources/GRPCProtobuf/Errors/RPCError+GoogleRPCStatus.swift index 56e9025..7a6e122 100644 --- a/Sources/GRPCProtobuf/Errors/RPCError+GoogleRPCStatus.swift +++ b/Sources/GRPCProtobuf/Errors/RPCError+GoogleRPCStatus.swift @@ -17,10 +17,12 @@ public import GRPCCore internal import SwiftProtobuf +@available(gRPCSwiftProtobuf 1.0, *) extension Metadata { static let statusDetailsBinKey = "grpc-status-details-bin" } +@available(gRPCSwiftProtobuf 1.0, *) extension RPCError { /// Unpack a ``GoogleRPCStatus`` error from the error metadata. /// diff --git a/Sources/GRPCProtobufCodeGen/ProtobufCodeGenParser.swift b/Sources/GRPCProtobufCodeGen/ProtobufCodeGenParser.swift index 3c4d9bc..6f61d76 100644 --- a/Sources/GRPCProtobufCodeGen/ProtobufCodeGenParser.swift +++ b/Sources/GRPCProtobufCodeGen/ProtobufCodeGenParser.swift @@ -32,6 +32,7 @@ internal import struct Foundation.IndexPath #endif /// Parses a ``FileDescriptor`` object into a ``CodeGenerationRequest`` object. +@available(gRPCSwiftProtobuf 1.0, *) package struct ProtobufCodeGenParser { let extraModuleImports: [String] let protoToModuleMappings: ProtoFileToModuleMappings @@ -99,6 +100,7 @@ package struct ProtobufCodeGenParser { } } +@available(gRPCSwiftProtobuf 1.0, *) extension ProtobufCodeGenParser { fileprivate func codeDependencies(file: FileDescriptor) -> [Dependency] { guard file.services.count > 0 else { @@ -141,6 +143,7 @@ extension ProtobufCodeGenParser { } } +@available(gRPCSwiftProtobuf 1.0, *) extension GRPCCodeGen.ServiceDescriptor { fileprivate init( descriptor: SwiftProtobufPluginLibrary.ServiceDescriptor, @@ -168,6 +171,7 @@ extension GRPCCodeGen.ServiceDescriptor { } } +@available(gRPCSwiftProtobuf 1.0, *) extension GRPCCodeGen.MethodDescriptor { fileprivate init( descriptor: SwiftProtobufPluginLibrary.MethodDescriptor, diff --git a/Sources/GRPCProtobufCodeGen/ProtobufCodeGenerator.swift b/Sources/GRPCProtobufCodeGen/ProtobufCodeGenerator.swift index 55307c7..ef15404 100644 --- a/Sources/GRPCProtobufCodeGen/ProtobufCodeGenerator.swift +++ b/Sources/GRPCProtobufCodeGen/ProtobufCodeGenerator.swift @@ -17,6 +17,7 @@ package import GRPCCodeGen package import SwiftProtobufPluginLibrary +@available(gRPCSwiftProtobuf 1.0, *) package struct ProtobufCodeGenerator { internal var config: ProtobufCodeGenerator.Config @@ -66,6 +67,7 @@ package struct ProtobufCodeGenerator { } } +@available(gRPCSwiftProtobuf 1.0, *) extension ProtobufCodeGenerator { package struct Config { package var accessLevel: GRPCCodeGen.CodeGenerator.Config.AccessLevel diff --git a/Sources/protoc-gen-grpc-swift/GenerateGRPC.swift b/Sources/protoc-gen-grpc-swift/GenerateGRPC.swift index b6299b2..2272e65 100644 --- a/Sources/protoc-gen-grpc-swift/GenerateGRPC.swift +++ b/Sources/protoc-gen-grpc-swift/GenerateGRPC.swift @@ -26,6 +26,7 @@ import Foundation #endif @main +@available(gRPCSwiftProtobuf 1.0, *) final class GenerateGRPC: SwiftProtobufPluginLibrary.CodeGenerator { var version: String? { Version.versionString @@ -81,6 +82,7 @@ final class GenerateGRPC: SwiftProtobufPluginLibrary.CodeGenerator { } } +@available(gRPCSwiftProtobuf 1.0, *) extension GenerateGRPC { private func uniqueOutputFileName( fileDescriptor: FileDescriptor, diff --git a/Sources/protoc-gen-grpc-swift/Options.swift b/Sources/protoc-gen-grpc-swift/Options.swift index 5907ab1..f32f2e6 100644 --- a/Sources/protoc-gen-grpc-swift/Options.swift +++ b/Sources/protoc-gen-grpc-swift/Options.swift @@ -48,6 +48,7 @@ enum FileNaming: String { case dropPath = "DropPath" } +@available(gRPCSwiftProtobuf 1.0, *) struct GeneratorOptions { private(set) var protoToModuleMappings = ProtoFileToModuleMappings() private(set) var fileNaming = FileNaming.fullPath @@ -191,6 +192,7 @@ struct GeneratorOptions { } } +@available(gRPCSwiftProtobuf 1.0, *) extension String.SubSequence { func trimmingWhitespaceAndNewlines() -> String { let trimmedSuffix = self.drop(while: { $0.isNewline || $0.isWhitespace }) @@ -199,6 +201,7 @@ extension String.SubSequence { } } +@available(gRPCSwiftProtobuf 1.0, *) extension GRPCCodeGen.CodeGenerator.Config.AccessLevel { fileprivate init?(protocOption value: String) { switch value { diff --git a/Tests/GRPCProtobufCodeGenTests/ProtobufCodeGenParserTests.swift b/Tests/GRPCProtobufCodeGenTests/ProtobufCodeGenParserTests.swift index c447588..13ee37e 100644 --- a/Tests/GRPCProtobufCodeGenTests/ProtobufCodeGenParserTests.swift +++ b/Tests/GRPCProtobufCodeGenTests/ProtobufCodeGenParserTests.swift @@ -27,20 +27,23 @@ struct ProtobufCodeGenParserTests { static let descriptorSetName = "test-service" static let fileDescriptorName = "test-service" - let codeGen: CodeGenerationRequest - - init() throws { - let descriptor = try Self.fileDescriptor - self.codeGen = try parseDescriptor(descriptor) + @available(gRPCSwiftProtobuf 1.0, *) + var codeGen: CodeGenerationRequest { + get throws { + let descriptor = try Self.fileDescriptor + return try parseDescriptor(descriptor) + } } @Test("Filename") - func fileName() { - #expect(self.codeGen.fileName == "test-service.proto") + @available(gRPCSwiftProtobuf 1.0, *) + func fileName() throws { + #expect(try self.codeGen.fileName == "test-service.proto") } @Test("Leading trivia") - func leadingTrivia() { + @available(gRPCSwiftProtobuf 1.0, *) + func leadingTrivia() throws { let expected = """ /// Leading trivia. @@ -56,95 +59,112 @@ struct ProtobufCodeGenParserTests { """ - #expect(self.codeGen.leadingTrivia == expected) + #expect(try self.codeGen.leadingTrivia == expected) } @Test("Dependencies") - func dependencies() { + @available(gRPCSwiftProtobuf 1.0, *) + func dependencies() throws { let expected: [GRPCCodeGen.Dependency] = [ .init(module: "GRPCProtobuf", accessLevel: .internal), // Always an internal import .init(module: "SwiftProtobuf", accessLevel: .internal), ] - #expect(self.codeGen.dependencies == expected) + #expect(try self.codeGen.dependencies == expected) } @Suite("Service") struct Service { - let service: GRPCCodeGen.ServiceDescriptor - - init() throws { - let request = try parseDescriptor(try TestService.fileDescriptor) - try #require(request.services.count == 1) - self.service = try #require(request.services.first) + @available(gRPCSwiftProtobuf 1.0, *) + var service: GRPCCodeGen.ServiceDescriptor { + get throws { + let request = try parseDescriptor(try TestService.fileDescriptor) + try #require(request.services.count == 1) + return try #require(request.services.first) + } } @Test("Name") - func name() { - #expect(self.service.name.identifyingName == "test.TestService") + @available(gRPCSwiftProtobuf 1.0, *) + func name() throws { + #expect(try self.service.name.identifyingName == "test.TestService") } @Suite("Methods") struct Methods { - let unary: GRPCCodeGen.MethodDescriptor - let clientStreaming: GRPCCodeGen.MethodDescriptor - let serverStreaming: GRPCCodeGen.MethodDescriptor - let bidiStreaming: GRPCCodeGen.MethodDescriptor - - init() throws { - let request = try parseDescriptor(try TestService.fileDescriptor) - #expect(request.services.count == 1) - let service = try #require(request.services.first) + @available(gRPCSwiftProtobuf 1.0, *) + var service: GRPCCodeGen.ServiceDescriptor { + get throws { + let request = try parseDescriptor(try TestService.fileDescriptor) + try #require(request.services.count == 1) + return try #require(request.services.first) + } + } - self.unary = service.methods[0] - self.clientStreaming = service.methods[1] - self.serverStreaming = service.methods[2] - self.bidiStreaming = service.methods[3] + @available(gRPCSwiftProtobuf 1.0, *) + var unary: GRPCCodeGen.MethodDescriptor { + get throws { try self.service.methods[0] } + } + @available(gRPCSwiftProtobuf 1.0, *) + var clientStreaming: GRPCCodeGen.MethodDescriptor { + get throws { try self.service.methods[1] } + } + @available(gRPCSwiftProtobuf 1.0, *) + var serverStreaming: GRPCCodeGen.MethodDescriptor { + get throws { try self.service.methods[2] } + } + @available(gRPCSwiftProtobuf 1.0, *) + var bidiStreaming: GRPCCodeGen.MethodDescriptor { + get throws { try self.service.methods[3] } } @Test("Documentation") - func documentation() { - #expect(self.unary.documentation == "/// Unary docs.\n") - #expect(self.clientStreaming.documentation == "/// Client streaming docs.\n") - #expect(self.serverStreaming.documentation == "/// Server streaming docs.\n") - #expect(self.bidiStreaming.documentation == "/// Bidirectional streaming docs.\n") + @available(gRPCSwiftProtobuf 1.0, *) + func documentation() throws { + #expect(try self.unary.documentation == "/// Unary docs.\n") + #expect(try self.clientStreaming.documentation == "/// Client streaming docs.\n") + #expect(try self.serverStreaming.documentation == "/// Server streaming docs.\n") + #expect(try self.bidiStreaming.documentation == "/// Bidirectional streaming docs.\n") } @Test("Name") - func name() { - #expect(self.unary.name.identifyingName == "Unary") - #expect(self.clientStreaming.name.identifyingName == "ClientStreaming") - #expect(self.serverStreaming.name.identifyingName == "ServerStreaming") - #expect(self.bidiStreaming.name.identifyingName == "BidirectionalStreaming") + @available(gRPCSwiftProtobuf 1.0, *) + func name() throws { + try #expect(self.unary.name.identifyingName == "Unary") + try #expect(self.clientStreaming.name.identifyingName == "ClientStreaming") + try #expect(self.serverStreaming.name.identifyingName == "ServerStreaming") + try #expect(self.bidiStreaming.name.identifyingName == "BidirectionalStreaming") } @Test("Input") - func input() { - #expect(self.unary.inputType == "Test_TestInput") - #expect(!self.unary.isInputStreaming) + @available(gRPCSwiftProtobuf 1.0, *) + func input() throws { + #expect(try self.unary.inputType == "Test_TestInput") + #expect(try !self.unary.isInputStreaming) - #expect(self.clientStreaming.inputType == "Test_TestInput") - #expect(self.clientStreaming.isInputStreaming) + #expect(try self.clientStreaming.inputType == "Test_TestInput") + #expect(try self.clientStreaming.isInputStreaming) - #expect(self.serverStreaming.inputType == "Test_TestInput") - #expect(!self.serverStreaming.isInputStreaming) + #expect(try self.serverStreaming.inputType == "Test_TestInput") + #expect(try !self.serverStreaming.isInputStreaming) - #expect(self.bidiStreaming.inputType == "Test_TestInput") - #expect(self.bidiStreaming.isInputStreaming) + #expect(try self.bidiStreaming.inputType == "Test_TestInput") + #expect(try self.bidiStreaming.isInputStreaming) } @Test("Output") - func output() { - #expect(self.unary.outputType == "Test_TestOutput") - #expect(!self.unary.isOutputStreaming) + @available(gRPCSwiftProtobuf 1.0, *) + func output() throws { + #expect(try self.unary.outputType == "Test_TestOutput") + #expect(try !self.unary.isOutputStreaming) - #expect(self.clientStreaming.outputType == "Test_TestOutput") - #expect(!self.clientStreaming.isOutputStreaming) + #expect(try self.clientStreaming.outputType == "Test_TestOutput") + #expect(try !self.clientStreaming.isOutputStreaming) - #expect(self.serverStreaming.outputType == "Test_TestOutput") - #expect(self.serverStreaming.isOutputStreaming) + #expect(try self.serverStreaming.outputType == "Test_TestOutput") + #expect(try self.serverStreaming.isOutputStreaming) - #expect(self.bidiStreaming.outputType == "Test_TestOutput") - #expect(self.bidiStreaming.isOutputStreaming) + #expect(try self.bidiStreaming.outputType == "Test_TestOutput") + #expect(try self.bidiStreaming.isOutputStreaming) } } } @@ -155,51 +175,58 @@ struct ProtobufCodeGenParserTests { static let descriptorSetName = "foo-service" static let fileDescriptorName = "foo-service" - let codeGen: CodeGenerationRequest - - init() throws { - let descriptor = try Self.fileDescriptor - self.codeGen = try parseDescriptor(descriptor) + @available(gRPCSwiftProtobuf 1.0, *) + var codeGen: CodeGenerationRequest { + get throws { + let descriptor = try Self.fileDescriptor + return try parseDescriptor(descriptor) + } } @Test("Name") - func name() { - #expect(self.codeGen.fileName == "foo-service.proto") + @available(gRPCSwiftProtobuf 1.0, *) + func name() throws { + #expect(try self.codeGen.fileName == "foo-service.proto") } @Test("Dependencies") - func dependencies() { + @available(gRPCSwiftProtobuf 1.0, *) + func dependencies() throws { let expected: [GRPCCodeGen.Dependency] = [ .init(module: "GRPCProtobuf", accessLevel: .internal) // Always an internal import ] - #expect(self.codeGen.dependencies == expected) + #expect(try self.codeGen.dependencies == expected) } @Test("Service1") + @available(gRPCSwiftProtobuf 1.0, *) func service1() throws { - let service = self.codeGen.services[0] + let service = try self.codeGen.services[0] #expect(service.name.identifyingName == "foo.FooService1") #expect(service.methods.count == 1) } @Test("Service1.Method") + @available(gRPCSwiftProtobuf 1.0, *) func service1Method() throws { - let method = self.codeGen.services[0].methods[0] + let method = try self.codeGen.services[0].methods[0] #expect(method.name.identifyingName == "Foo") #expect(method.inputType == "Foo_FooInput") #expect(method.outputType == "Foo_FooOutput") } @Test("Service2") + @available(gRPCSwiftProtobuf 1.0, *) func service2() throws { - let service = self.codeGen.services[1] + let service = try self.codeGen.services[1] #expect(service.name.identifyingName == "foo.FooService2") #expect(service.methods.count == 1) } @Test("Service2.Method") + @available(gRPCSwiftProtobuf 1.0, *) func service2Method() throws { - let method = self.codeGen.services[1].methods[0] + let method = try self.codeGen.services[1].methods[0] #expect(method.name.identifyingName == "Foo") #expect(method.inputType == "Foo_FooInput") #expect(method.outputType == "Foo_FooOutput") @@ -211,18 +238,14 @@ struct ProtobufCodeGenParserTests { static var descriptorSetName: String { "bar-service" } static var fileDescriptorName: String { "bar-service" } - let codeGen: CodeGenerationRequest - let service: GRPCCodeGen.ServiceDescriptor - - init() throws { + @Test("Service name") + @available(gRPCSwiftProtobuf 1.0, *) + func serviceName() throws { let descriptor = try Self.fileDescriptor - self.codeGen = try parseDescriptor(descriptor) - self.service = try #require(self.codeGen.services.first) - } + let codeGen = try parseDescriptor(descriptor) + let service = try #require(codeGen.services.first) - @Test("Service name") - func serviceName() { - #expect(self.service.name.identifyingName == "BarService") + #expect(service.name.identifyingName == "BarService") } } @@ -231,20 +254,17 @@ struct ProtobufCodeGenParserTests { static let descriptorSetName = "wkt-service" static let fileDescriptorName = "wkt-service" - let codeGen: CodeGenerationRequest - - init() throws { + @Test("Dependencies") + @available(gRPCSwiftProtobuf 1.0, *) + func dependencies() throws { let descriptor = try Self.fileDescriptor - self.codeGen = try parseDescriptor(descriptor) - } + let codeGen = try parseDescriptor(descriptor) - @Test("Dependencies") - func dependencies() { let expected: [Dependency] = [ Dependency(module: "GRPCProtobuf", accessLevel: .internal), Dependency(module: "SwiftProtobuf", accessLevel: .internal), ] - #expect(self.codeGen.dependencies == expected) + #expect(codeGen.dependencies == expected) } } } diff --git a/Tests/GRPCProtobufCodeGenTests/ProtobufCodeGeneratorTests.swift b/Tests/GRPCProtobufCodeGenTests/ProtobufCodeGeneratorTests.swift index 86e5ec4..414dd5f 100644 --- a/Tests/GRPCProtobufCodeGenTests/ProtobufCodeGeneratorTests.swift +++ b/Tests/GRPCProtobufCodeGenTests/ProtobufCodeGeneratorTests.swift @@ -48,6 +48,7 @@ struct ProtobufCodeGeneratorTests { } } + @available(gRPCSwiftProtobuf 1.0, *) @Test( "Generate", arguments: [CodeGenerator.Config.AccessLevel.internal], @@ -1103,6 +1104,7 @@ struct ProtobufCodeGeneratorTests { } @Test("Generate with different module names") + @available(gRPCSwiftProtobuf 1.0, *) func generateWithDifferentModuleNames() throws { var config = ProtobufCodeGenerator.Config.defaults let defaultNames = config.moduleNames @@ -1138,6 +1140,7 @@ struct ProtobufCodeGeneratorTests { static let fileDescriptorName = "foo-messages" @Test("Generate") + @available(gRPCSwiftProtobuf 1.0, *) func generate() throws { var config: ProtobufCodeGenerator.Config = .defaults config.accessLevel = .public diff --git a/Tests/GRPCProtobufCodeGenTests/Utilities.swift b/Tests/GRPCProtobufCodeGenTests/Utilities.swift index de227e7..b8c617a 100644 --- a/Tests/GRPCProtobufCodeGenTests/Utilities.swift +++ b/Tests/GRPCProtobufCodeGenTests/Utilities.swift @@ -68,6 +68,7 @@ private func loadDescriptorSet( return DescriptorSet(proto: descriptorSet) } +@available(gRPCSwiftProtobuf 1.0, *) func parseDescriptor( _ descriptor: FileDescriptor, extraModuleImports: [String] = [], diff --git a/Tests/GRPCProtobufTests/Errors/DetailedErrorTests.swift b/Tests/GRPCProtobufTests/Errors/DetailedErrorTests.swift index 7c49075..c9a77da 100644 --- a/Tests/GRPCProtobufTests/Errors/DetailedErrorTests.swift +++ b/Tests/GRPCProtobufTests/Errors/DetailedErrorTests.swift @@ -40,6 +40,7 @@ struct DetailedErrorTests { (["Help", "Help", "Help"], [.help(.testValue), .help(.testValue), .help(.testValue)]), ] as [([String], [ErrorDetails])] ) + @available(gRPCSwiftProtobuf 1.0, *) func rpcStatus(details: [String], expected: [ErrorDetails]) async throws { let inProcess = InProcessTransport() try await withGRPCServer(transport: inProcess.server, services: [ErrorThrowingService()]) { _ in @@ -95,11 +96,13 @@ struct DetailedErrorTests { (.localizedMessage(.testValue), #"LocalizedMessage(locale: "l", message: "m")"#), ] as [(ErrorDetails, String)] ) + @available(gRPCSwiftProtobuf 1.0, *) func errorInfoDescription(_ details: ErrorDetails, expected: String) { #expect(String(describing: details) == expected) } @Test("Round-trip encoding of GoogleRPCStatus") + @available(gRPCSwiftProtobuf 1.0, *) func googleRPCStatusRoundTripCoding() throws { let detail = ErrorDetails.BadRequest(violations: [.init(field: "foo", description: "bar")]) let status = GoogleRPCStatus(code: .dataLoss, message: "Uh oh", details: [.badRequest(detail)]) @@ -113,6 +116,7 @@ struct DetailedErrorTests { } } +@available(gRPCSwiftProtobuf 1.0, *) private struct ErrorThrowingService: ErrorService.SimpleServiceProtocol { func throwError( request: ThrowInput, @@ -183,14 +187,17 @@ private struct ErrorThrowingService: ErrorService.SimpleServiceProtocol { } } +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails.ErrorInfo { fileprivate static let testValue = Self(reason: "r", domain: "d", metadata: ["k": "v"]) } +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails.RetryInfo { fileprivate static let testValue = Self(delay: .seconds(1)) } +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails.DebugInfo { fileprivate static let testValue = Self( stack: ["foo.foo()", "foo.bar()"], @@ -198,6 +205,7 @@ extension ErrorDetails.DebugInfo { ) } +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails.QuotaFailure { fileprivate static let testValue = Self( violations: [ @@ -206,6 +214,7 @@ extension ErrorDetails.QuotaFailure { ) } +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails.PreconditionFailure { fileprivate static let testValue = Self( violations: [ @@ -214,6 +223,7 @@ extension ErrorDetails.PreconditionFailure { ) } +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails.BadRequest { fileprivate static let testValue = Self( violations: [ @@ -222,14 +232,17 @@ extension ErrorDetails.BadRequest { ) } +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails.RequestInfo { fileprivate static let testValue = Self(requestID: "id", servingData: "d") } +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails.ResourceInfo { fileprivate static let testValue = Self(type: "t", name: "n", errorDescription: "d") } +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails.Help { fileprivate static let testValue = Self( links: [ @@ -238,6 +251,7 @@ extension ErrorDetails.Help { ) } +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorDetails.LocalizedMessage { fileprivate static let testValue = Self(locale: "l", message: "m") } diff --git a/Tests/GRPCProtobufTests/Errors/Generated/error-service.grpc.swift b/Tests/GRPCProtobufTests/Errors/Generated/error-service.grpc.swift index 9096079..c675a71 100644 --- a/Tests/GRPCProtobufTests/Errors/Generated/error-service.grpc.swift +++ b/Tests/GRPCProtobufTests/Errors/Generated/error-service.grpc.swift @@ -30,7 +30,7 @@ internal import SwiftProtobuf // MARK: - ErrorService /// Namespace containing generated types for the "ErrorService" service. -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +@available(gRPCSwiftProtobuf 1.0, *) internal enum ErrorService { /// Service descriptor for the "ErrorService" service. internal static let descriptor = GRPCCore.ServiceDescriptor(fullyQualifiedService: "ErrorService") @@ -55,7 +55,7 @@ internal enum ErrorService { } } -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +@available(gRPCSwiftProtobuf 1.0, *) extension GRPCCore.ServiceDescriptor { /// Service descriptor for the "ErrorService" service. internal static let ErrorService = GRPCCore.ServiceDescriptor(fullyQualifiedService: "ErrorService") @@ -63,7 +63,7 @@ extension GRPCCore.ServiceDescriptor { // MARK: ErrorService (server) -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorService { /// Streaming variant of the service protocol for the "ErrorService" service. /// @@ -137,7 +137,7 @@ extension ErrorService { } // Default implementation of 'registerMethods(with:)'. -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorService.StreamingServiceProtocol { internal func registerMethods(with router: inout GRPCCore.RPCRouter) where Transport: GRPCCore.ServerTransport { router.registerHandler( @@ -155,7 +155,7 @@ extension ErrorService.StreamingServiceProtocol { } // Default implementation of streaming methods from 'StreamingServiceProtocol'. -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorService.ServiceProtocol { internal func throwError( request: GRPCCore.StreamingServerRequest, @@ -170,7 +170,7 @@ extension ErrorService.ServiceProtocol { } // Default implementation of methods from 'ServiceProtocol'. -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorService.SimpleServiceProtocol { internal func throwError( request: GRPCCore.ServerRequest, @@ -188,7 +188,7 @@ extension ErrorService.SimpleServiceProtocol { // MARK: ErrorService (client) -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorService { /// Generated client protocol for the "ErrorService" service. /// @@ -264,7 +264,7 @@ extension ErrorService { } // Helpers providing default arguments to 'ClientProtocol' methods. -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorService.ClientProtocol { /// Call the "ThrowError" method. /// @@ -293,7 +293,7 @@ extension ErrorService.ClientProtocol { } // Helpers providing sugared APIs for 'ClientProtocol' methods. -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +@available(gRPCSwiftProtobuf 1.0, *) extension ErrorService.ClientProtocol { /// Call the "ThrowError" method. /// diff --git a/Tests/GRPCProtobufTests/ProtobufCodingTests.swift b/Tests/GRPCProtobufTests/ProtobufCodingTests.swift index 6f30adf..9306238 100644 --- a/Tests/GRPCProtobufTests/ProtobufCodingTests.swift +++ b/Tests/GRPCProtobufTests/ProtobufCodingTests.swift @@ -19,6 +19,7 @@ import GRPCProtobuf import SwiftProtobuf import XCTest +@available(gRPCSwiftProtobuf 1.0, *) final class ProtobufCodingTests: XCTestCase { func testSerializeDeserializeRoundtrip() throws { let message = Google_Protobuf_Timestamp.with { @@ -73,6 +74,7 @@ final class ProtobufCodingTests: XCTestCase { } } +@available(gRPCSwiftProtobuf 1.0, *) struct TestMessage: SwiftProtobuf.Message { var text: String = "" var unknownFields = SwiftProtobuf.UnknownStorage() diff --git a/dev/protos/generate.sh b/dev/protos/generate.sh index e9244aa..5af02e8 100755 --- a/dev/protos/generate.sh +++ b/dev/protos/generate.sh @@ -94,7 +94,7 @@ function generate_error_service { output="$root/Tests/GRPCProtobufTests/Errors/Generated" generate_message "$proto" "$(dirname "$proto")" "$output" "Visibility=Internal" "UseAccessLevelOnImports=true" - generate_grpc "$proto" "$(dirname "$proto")" "$output" "Visibility=Internal" "UseAccessLevelOnImports=true" + generate_grpc "$proto" "$(dirname "$proto")" "$output" "Visibility=Internal" "UseAccessLevelOnImports=true" "Availability=gRPCSwiftProtobuf 1.0" } #- DESCRIPTOR SETS ------------------------------------------------------------