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
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ test-echo: all
kill -9 `cat echo.pid`
diff -u test.out Sources/Examples/Echo/test.gold

test-plugin: all
test-plugin:
swift build -v $(CFLAGS) --product protoc-gen-swiftgrpc
protoc Sources/Examples/Echo/echo.proto --proto_path=Sources/Examples/Echo --plugin=.build/debug/protoc-gen-swift --plugin=.build/debug/protoc-gen-swiftgrpc --swiftgrpc_out=/tmp --swiftgrpc_opt=TestStubs=true
diff -u /tmp/echo.grpc.swift Sources/Examples/Echo/Generated/echo.grpc.swift

Expand Down
72 changes: 56 additions & 16 deletions Sources/Examples/Echo/Generated/echo.grpc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,17 @@ fileprivate final class Echo_EchoGetCallBase: ClientCallUnaryBase<Echo_EchoReque
}

internal protocol Echo_EchoExpandCall: ClientCallServerStreaming {
/// Call this to wait for a result. Blocking.
func receive() throws -> Echo_EchoResponse?
/// Do not call this directly, call `receive()` in the protocol extension below instead.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be clarified with "private" access control?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wish I could, but protocols can't have access modifiers for individual methods. And given that the extension (with the default arguments) should be public, the helper method needs to be, too :-( If someone knows a better way to do this, I'd love to change it!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What benefit do we get from defining methods in protocol extensions instead of generating them directly? It seems that if we generated these methods, then we wouldn't need to expose the "Internal" versions in the protocols.

Would it be idiomatic Swift to use an underscore prefix instead of "Internal" in the helper method names? e.g. "_receive" instead of "receiveInternal"? The leading underscore seems like a more widely-understood indicator that something is private.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As explained before, methods declared in protocols can’t have default arguments, while methods declared in protocol extensions can. So we need to have a method in the extension unless we want users to provide all method arguments for each call.

Good point about the underscores, will look into that.

func _receive(timeout: DispatchTime) throws -> Echo_EchoResponse?
/// Call this to wait for a result. Nonblocking.
func receive(completion: @escaping (ResultOrRPCError<Echo_EchoResponse?>) -> Void) throws
}

internal extension Echo_EchoExpandCall {
/// Call this to wait for a result. Blocking.
func receive(timeout: DispatchTime = .distantFuture) throws -> Echo_EchoResponse? { return try self._receive(timeout: timeout) }
}

fileprivate final class Echo_EchoExpandCallBase: ClientCallServerStreamingBase<Echo_EchoRequest, Echo_EchoResponse>, Echo_EchoExpandCall {
override class var method: String { return "/echo.Echo/Expand" }
}
Expand All @@ -49,15 +54,20 @@ class Echo_EchoExpandCallTestStub: ClientCallServerStreamingTestStub<Echo_EchoRe
internal protocol Echo_EchoCollectCall: ClientCallClientStreaming {
/// Send a message to the stream. Nonblocking.
func send(_ message: Echo_EchoRequest, completion: @escaping (Error?) -> Void) throws
/// Send a message to the stream and wait for the send operation to finish. Blocking.
func send(_ message: Echo_EchoRequest) throws
/// Do not call this directly, call `send()` in the protocol extension below instead.
func _send(_ message: Echo_EchoRequest, timeout: DispatchTime) throws

/// Call this to close the connection and wait for a response. Blocking.
func closeAndReceive() throws -> Echo_EchoResponse
/// Call this to close the connection and wait for a response. Nonblocking.
func closeAndReceive(completion: @escaping (ResultOrRPCError<Echo_EchoResponse>) -> Void) throws
}

internal extension Echo_EchoCollectCall {
/// Send a message to the stream and wait for the send operation to finish. Blocking.
func send(_ message: Echo_EchoRequest, timeout: DispatchTime = .distantFuture) throws { try self._send(message, timeout: timeout) }
}

fileprivate final class Echo_EchoCollectCallBase: ClientCallClientStreamingBase<Echo_EchoRequest, Echo_EchoResponse>, Echo_EchoCollectCall {
override class var method: String { return "/echo.Echo/Collect" }
}
Expand All @@ -69,22 +79,32 @@ class Echo_EchoCollectCallTestStub: ClientCallClientStreamingTestStub<Echo_EchoR
}

internal protocol Echo_EchoUpdateCall: ClientCallBidirectionalStreaming {
/// Call this to wait for a result. Blocking.
func receive() throws -> Echo_EchoResponse?
/// Do not call this directly, call `receive()` in the protocol extension below instead.
func _receive(timeout: DispatchTime) throws -> Echo_EchoResponse?
/// Call this to wait for a result. Nonblocking.
func receive(completion: @escaping (ResultOrRPCError<Echo_EchoResponse?>) -> Void) throws

/// Send a message to the stream. Nonblocking.
func send(_ message: Echo_EchoRequest, completion: @escaping (Error?) -> Void) throws
/// Send a message to the stream and wait for the send operation to finish. Blocking.
func send(_ message: Echo_EchoRequest) throws
/// Do not call this directly, call `send()` in the protocol extension below instead.
func _send(_ message: Echo_EchoRequest, timeout: DispatchTime) throws

/// Call this to close the sending connection. Blocking.
func closeSend() throws
/// Call this to close the sending connection. Nonblocking.
func closeSend(completion: (() -> Void)?) throws
}

internal extension Echo_EchoUpdateCall {
/// Call this to wait for a result. Blocking.
func receive(timeout: DispatchTime = .distantFuture) throws -> Echo_EchoResponse? { return try self._receive(timeout: timeout) }
}

internal extension Echo_EchoUpdateCall {
/// Send a message to the stream and wait for the send operation to finish. Blocking.
func send(_ message: Echo_EchoRequest, timeout: DispatchTime = .distantFuture) throws { try self._send(message, timeout: timeout) }
}

fileprivate final class Echo_EchoUpdateCallBase: ClientCallBidirectionalStreamingBase<Echo_EchoRequest, Echo_EchoResponse>, Echo_EchoUpdateCall {
override class var method: String { return "/echo.Echo/Update" }
}
Expand Down Expand Up @@ -207,21 +227,26 @@ class Echo_EchoGetSessionTestStub: ServerSessionUnaryTestStub, Echo_EchoGetSessi
internal protocol Echo_EchoExpandSession: ServerSessionServerStreaming {
/// Send a message to the stream. Nonblocking.
func send(_ message: Echo_EchoResponse, completion: @escaping (Error?) -> Void) throws
/// Send a message to the stream and wait for the send operation to finish. Blocking.
func send(_ message: Echo_EchoResponse) throws
/// Do not call this directly, call `send()` in the protocol extension below instead.
func _send(_ message: Echo_EchoResponse, timeout: DispatchTime) throws

/// Close the connection and send the status. Non-blocking.
/// You MUST call this method once you are done processing the request.
func close(withStatus status: ServerStatus, completion: (() -> Void)?) throws
}

internal extension Echo_EchoExpandSession {
/// Send a message to the stream and wait for the send operation to finish. Blocking.
func send(_ message: Echo_EchoResponse, timeout: DispatchTime = .distantFuture) throws { try self._send(message, timeout: timeout) }
}

fileprivate final class Echo_EchoExpandSessionBase: ServerSessionServerStreamingBase<Echo_EchoRequest, Echo_EchoResponse>, Echo_EchoExpandSession {}

class Echo_EchoExpandSessionTestStub: ServerSessionServerStreamingTestStub<Echo_EchoResponse>, Echo_EchoExpandSession {}

internal protocol Echo_EchoCollectSession: ServerSessionClientStreaming {
/// Call this to wait for a result. Blocking.
func receive() throws -> Echo_EchoRequest?
/// Do not call this directly, call `receive()` in the protocol extension below instead.
func _receive(timeout: DispatchTime) throws -> Echo_EchoRequest?
/// Call this to wait for a result. Nonblocking.
func receive(completion: @escaping (ResultOrRPCError<Echo_EchoRequest?>) -> Void) throws

Expand All @@ -234,26 +259,41 @@ internal protocol Echo_EchoCollectSession: ServerSessionClientStreaming {
func sendErrorAndClose(status: ServerStatus, completion: (() -> Void)?) throws
}

internal extension Echo_EchoCollectSession {
/// Call this to wait for a result. Blocking.
func receive(timeout: DispatchTime = .distantFuture) throws -> Echo_EchoRequest? { return try self._receive(timeout: timeout) }
}

fileprivate final class Echo_EchoCollectSessionBase: ServerSessionClientStreamingBase<Echo_EchoRequest, Echo_EchoResponse>, Echo_EchoCollectSession {}

class Echo_EchoCollectSessionTestStub: ServerSessionClientStreamingTestStub<Echo_EchoRequest, Echo_EchoResponse>, Echo_EchoCollectSession {}

internal protocol Echo_EchoUpdateSession: ServerSessionBidirectionalStreaming {
/// Call this to wait for a result. Blocking.
func receive() throws -> Echo_EchoRequest?
/// Do not call this directly, call `receive()` in the protocol extension below instead.
func _receive(timeout: DispatchTime) throws -> Echo_EchoRequest?
/// Call this to wait for a result. Nonblocking.
func receive(completion: @escaping (ResultOrRPCError<Echo_EchoRequest?>) -> Void) throws

/// Send a message to the stream. Nonblocking.
func send(_ message: Echo_EchoResponse, completion: @escaping (Error?) -> Void) throws
/// Send a message to the stream and wait for the send operation to finish. Blocking.
func send(_ message: Echo_EchoResponse) throws
/// Do not call this directly, call `send()` in the protocol extension below instead.
func _send(_ message: Echo_EchoResponse, timeout: DispatchTime) throws

/// Close the connection and send the status. Non-blocking.
/// You MUST call this method once you are done processing the request.
func close(withStatus status: ServerStatus, completion: (() -> Void)?) throws
}

internal extension Echo_EchoUpdateSession {
/// Call this to wait for a result. Blocking.
func receive(timeout: DispatchTime = .distantFuture) throws -> Echo_EchoRequest? { return try self._receive(timeout: timeout) }
}

internal extension Echo_EchoUpdateSession {
/// Send a message to the stream and wait for the send operation to finish. Blocking.
func send(_ message: Echo_EchoResponse, timeout: DispatchTime = .distantFuture) throws { try self._send(message, timeout: timeout) }
}

fileprivate final class Echo_EchoUpdateSessionBase: ServerSessionBidirectionalStreamingBase<Echo_EchoRequest, Echo_EchoResponse>, Echo_EchoUpdateSession {}

class Echo_EchoUpdateSessionTestStub: ServerSessionBidirectionalStreamingTestStub<Echo_EchoRequest, Echo_EchoResponse>, Echo_EchoUpdateSession {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,20 @@ open class ClientCallBidirectionalStreamingTestStub<InputType: Message, OutputTy

public init() {}

open func receive() throws -> OutputType? {
open func _receive(timeout: DispatchTime) throws -> OutputType? {
defer { if !outputs.isEmpty { outputs.removeFirst() } }
return outputs.first
}

open func receive(completion: @escaping (ResultOrRPCError<OutputType?>) -> Void) throws {
completion(.result(try self.receive()))
completion(.result(try self._receive(timeout: .distantFuture)))
}

open func send(_ message: InputType, completion _: @escaping (Error?) -> Void) throws {
inputs.append(message)
}

open func send(_ message: InputType) throws {
open func _send(_ message: InputType, timeout: DispatchTime) throws {
inputs.append(message)
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftGRPC/Runtime/ClientCallClientStreaming.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ open class ClientCallClientStreamingTestStub<InputType: Message, OutputType: Mes
inputs.append(message)
}

open func send(_ message: InputType) throws {
open func _send(_ message: InputType, timeout: DispatchTime) throws {
inputs.append(message)
}

Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftGRPC/Runtime/ClientCallServerStreaming.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ open class ClientCallServerStreamingTestStub<OutputType: Message>: ClientCallSer

public init() {}

open func receive() throws -> OutputType? {
open func _receive(timeout: DispatchTime) throws -> OutputType? {
defer { if !outputs.isEmpty { outputs.removeFirst() } }
return outputs.first
}

open func receive(completion: @escaping (ResultOrRPCError<OutputType?>) -> Void) throws {
completion(.result(try self.receive()))
completion(.result(try self._receive(timeout: .distantFuture)))
}

open func cancel() {}
Expand Down
3 changes: 2 additions & 1 deletion Sources/SwiftGRPC/Runtime/RPCError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ import Foundation
/// Type for errors thrown from generated client code.
public enum RPCError: Error {
case invalidMessageReceived
case timedOut
case callError(CallResult)
}

public extension RPCError {
var callResult: CallResult? {
switch self {
case .invalidMessageReceived: return nil
case .invalidMessageReceived, .timedOut: return nil
case .callError(let callResult): return callResult
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,20 +69,20 @@ open class ServerSessionBidirectionalStreamingTestStub<InputType: Message, Outpu
open var outputs: [OutputType] = []
open var status: ServerStatus?

open func receive() throws -> InputType? {
open func _receive(timeout: DispatchTime) throws -> InputType? {
defer { if !inputs.isEmpty { inputs.removeFirst() } }
return inputs.first
}

open func receive(completion: @escaping (ResultOrRPCError<InputType?>) -> Void) throws {
completion(.result(try self.receive()))
completion(.result(try self._receive(timeout: .distantFuture)))
}

open func send(_ message: OutputType, completion _: @escaping (Error?) -> Void) throws {
outputs.append(message)
}

open func send(_ message: OutputType) throws {
open func _send(_ message: OutputType, timeout: DispatchTime) throws {
outputs.append(message)
}

Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftGRPC/Runtime/ServerSessionClientStreaming.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,13 @@ open class ServerSessionClientStreamingTestStub<InputType: Message, OutputType:
open var output: OutputType?
open var status: ServerStatus?

open func receive() throws -> InputType? {
open func _receive(timeout: DispatchTime) throws -> InputType? {
defer { if !inputs.isEmpty { inputs.removeFirst() } }
return inputs.first
}

open func receive(completion: @escaping (ResultOrRPCError<InputType?>) -> Void) throws {
completion(.result(try self.receive()))
completion(.result(try self._receive(timeout: .distantFuture)))
}

open func sendAndClose(response: OutputType, status: ServerStatus, completion: (() -> Void)?) throws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ open class ServerSessionServerStreamingTestStub<OutputType: Message>: ServerSess
outputs.append(message)
}

open func send(_ message: OutputType) throws {
open func _send(_ message: OutputType, timeout: DispatchTime) throws {
outputs.append(message)
}

Expand Down
6 changes: 4 additions & 2 deletions Sources/SwiftGRPC/Runtime/StreamReceiving.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,16 @@ extension StreamReceiving {
}
}

public func receive() throws -> ReceivedType? {
public func _receive(timeout: DispatchTime) throws -> ReceivedType? {
var result: ResultOrRPCError<ReceivedType?>?
let sem = DispatchSemaphore(value: 0)
try receive {
result = $0
sem.signal()
}
_ = sem.wait()
if sem.wait(timeout: timeout) == .timedOut {
throw RPCError.timedOut
}
switch result! {
case .result(let response): return response
case .error(let error): throw error
Expand Down
6 changes: 4 additions & 2 deletions Sources/SwiftGRPC/Runtime/StreamSending.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,16 @@ extension StreamSending {
try call.sendMessage(data: message.serializedData(), completion: completion)
}

public func send(_ message: SentType) throws {
public func _send(_ message: SentType, timeout: DispatchTime) throws {
var resultError: Error?
let sem = DispatchSemaphore(value: 0)
try send(message) {
resultError = $0
sem.signal()
}
_ = sem.wait()
if sem.wait(timeout: timeout) == .timedOut {
throw RPCError.timedOut
}
if let resultError = resultError {
throw resultError
}
Expand Down
8 changes: 8 additions & 0 deletions Sources/protoc-gen-swiftgrpc/Generator-Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ extension Generator {
outdent()
println("}")
println()
printStreamReceiveExtension(extendedType: callName, receivedType: methodOutputName)
println()
println("fileprivate final class \(callName)Base: ClientCallServerStreamingBase<\(methodInputName), \(methodOutputName)>, \(callName) {")
indent()
println("override class var method: String { return \(methodPath) }")
Expand Down Expand Up @@ -88,6 +90,8 @@ extension Generator {
outdent()
println("}")
println()
printStreamSendExtension(extendedType: callName, sentType: methodInputName)
println()
println("fileprivate final class \(callName)Base: ClientCallClientStreamingBase<\(methodInputName), \(methodOutputName)>, \(callName) {")
indent()
println("override class var method: String { return \(methodPath) }")
Expand Down Expand Up @@ -120,6 +124,10 @@ extension Generator {
outdent()
println("}")
println()
printStreamReceiveExtension(extendedType: callName, receivedType: methodOutputName)
println()
printStreamSendExtension(extendedType: callName, sentType: methodInputName)
println()
println("fileprivate final class \(callName)Base: ClientCallBidirectionalStreamingBase<\(methodInputName), \(methodOutputName)>, \(callName) {")
indent()
println("override class var method: String { return \(methodPath) }")
Expand Down
24 changes: 21 additions & 3 deletions Sources/protoc-gen-swiftgrpc/Generator-Methods.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,34 @@ import SwiftProtobufPluginLibrary

extension Generator {
func printStreamReceiveMethods(receivedType: String) {
println("/// Call this to wait for a result. Blocking.")
println("func receive() throws -> \(receivedType)?")
println("/// Do not call this directly, call `receive()` in the protocol extension below instead.")
println("func _receive(timeout: DispatchTime) throws -> \(receivedType)?")
println("/// Call this to wait for a result. Nonblocking.")
println("func receive(completion: @escaping (ResultOrRPCError<\(receivedType)?>) -> Void) throws")
}

func printStreamReceiveExtension(extendedType: String, receivedType: String) {
println("\(access) extension \(extendedType) {")
indent()
println("/// Call this to wait for a result. Blocking.")
println("func receive(timeout: DispatchTime = .distantFuture) throws -> \(receivedType)? { return try self._receive(timeout: timeout) }")
outdent()
println("}")
}

func printStreamSendMethods(sentType: String) {
println("/// Send a message to the stream. Nonblocking.")
println("func send(_ message: \(sentType), completion: @escaping (Error?) -> Void) throws")
println("/// Do not call this directly, call `send()` in the protocol extension below instead.")
println("func _send(_ message: \(sentType), timeout: DispatchTime) throws")
}

func printStreamSendExtension(extendedType: String,sentType: String) {
println("\(access) extension \(extendedType) {")
indent()
println("/// Send a message to the stream and wait for the send operation to finish. Blocking.")
println("func send(_ message: \(sentType)) throws")
println("func send(_ message: \(sentType), timeout: DispatchTime = .distantFuture) throws { try self._send(message, timeout: timeout) }")
outdent()
println("}")
}
}
Loading