Browse Source

Rework Operations into an enum with associated objects. (jconverse@)

Tim Burks 9 years ago
parent
commit
5158fab75e

+ 1 - 0
Packages/CgRPC/Sources/cgrpc.h

@@ -174,6 +174,7 @@ grpc_call_error cgrpc_call_perform(cgrpc_call *call, cgrpc_operations *operation
 
 // operations
 cgrpc_operations *cgrpc_operations_create();
+void cgrpc_operations_destroy(cgrpc_operations *operations);
 void cgrpc_operations_reserve_space_for_operations(cgrpc_operations *call, int max_operations);
 void cgrpc_operations_add_operation(cgrpc_operations *call, cgrpc_observer *observer);
 

+ 5 - 0
Packages/CgRPC/Sources/operations.c

@@ -41,6 +41,11 @@ cgrpc_operations *cgrpc_operations_create() {
   return (cgrpc_operations *) malloc(sizeof (cgrpc_operations));
 }
 
+void cgrpc_operations_destroy(cgrpc_operations *operations) {
+  free(operations->ops);
+  free(operations);
+}
+
 void cgrpc_operations_reserve_space_for_operations(cgrpc_operations *operations, int max_operations) {
   operations->ops = (grpc_op *) malloc(max_operations * sizeof(grpc_op));
   memset(operations->ops, 0, max_operations * sizeof(grpc_op));

+ 37 - 53
Packages/gRPC/Sources/Call.swift

@@ -170,31 +170,22 @@ public class Call {
   public func performNonStreamingCall(messageData: Data,
                                       metadata: Metadata,
                                       completion: @escaping CallCompletion) throws -> Void {
-
     let messageBuffer = ByteBuffer(data:messageData)
-
-    let operation_sendInitialMetadata = Operation_SendInitialMetadata(metadata:metadata);
-    let operation_sendMessage = Operation_SendMessage(message:messageBuffer)
-    let operation_sendCloseFromClient = Operation_SendCloseFromClient()
-    let operation_receiveInitialMetadata = Operation_ReceiveInitialMetadata()
-    let operation_receiveStatusOnClient = Operation_ReceiveStatusOnClient()
-    let operation_receiveMessage = Operation_ReceiveMessage()
-
     let operations = OperationGroup(call:self,
-                                    operations:[operation_sendInitialMetadata,
-                                                operation_sendMessage,
-                                                operation_sendCloseFromClient,
-                                                operation_receiveInitialMetadata,
-                                                operation_receiveStatusOnClient,
-                                                operation_receiveMessage],
+                                    operations:[.sendInitialMetadata(metadata),
+                                                .sendMessage(messageBuffer),
+                                                .sendCloseFromClient,
+                                                .receiveInitialMetadata,
+                                                .receiveStatusOnClient,
+                                                .receiveMessage],
                                     completion:
-      {(success) in
-        if success {
-          try completion(CallResult(statusCode:operation_receiveStatusOnClient.status(),
-                                    statusMessage:operation_receiveStatusOnClient.statusDetails(),
-                                    resultData:operation_receiveMessage.message()?.data(),
-                                    initialMetadata:operation_receiveInitialMetadata.metadata(),
-                                    trailingMetadata:operation_receiveStatusOnClient.metadata()))
+      {(operationGroup) in
+        if operationGroup.success {
+          try completion(CallResult(statusCode:operationGroup.receivedStatusCode()!,
+                                    statusMessage:operationGroup.receivedStatusMessage(),
+                                    resultData:operationGroup.receivedMessage()?.data(),
+                                    initialMetadata:operationGroup.receivedInitialMetadata(),
+                                    trailingMetadata:operationGroup.receivedTrailingMetadata()))
         } else {
           try completion(CallResult(statusCode:0,
                                     statusMessage:nil,
@@ -236,10 +227,9 @@ public class Call {
 
   private func sendWithoutBlocking(data: Data) throws -> Void {
     let messageBuffer = ByteBuffer(data:data)
-    let operation_sendMessage = Operation_SendMessage(message:messageBuffer)
-    let operations = OperationGroup(call:self, operations:[operation_sendMessage])
-    {(success) in
-      if success {
+    let operations = OperationGroup(call:self, operations:[.sendMessage(messageBuffer)])
+    {(operationGroup) in
+      if operationGroup.success {
         DispatchQueue.main.async {
           if self.pendingMessages.count > 0 {
             let nextMessage = self.pendingMessages.first!
@@ -260,14 +250,12 @@ public class Call {
     try self.perform(operations)
   }
 
-
   // receive a message over a streaming connection
   public func receiveMessage(callback:@escaping ((Data!) throws -> Void)) throws -> Void {
-    let operation_receiveMessage = Operation_ReceiveMessage()
-    let operations = OperationGroup(call:self, operations:[operation_receiveMessage])
-    {(success) in
-      if success {
-        if let messageBuffer = operation_receiveMessage.message() {
+    let operations = OperationGroup(call:self, operations:[.receiveMessage])
+    {(operationGroup) in
+      if operationGroup.success {
+        if let messageBuffer = operationGroup.receivedMessage() {
           try callback(messageBuffer.data())
         }
       }
@@ -277,10 +265,9 @@ public class Call {
 
   // send initial metadata over a streaming connection
   private func sendInitialMetadata(metadata: Metadata) throws -> Void {
-    let operation_sendInitialMetadata = Operation_SendInitialMetadata(metadata:metadata);
-    let operations = OperationGroup(call:self, operations:[operation_sendInitialMetadata])
-    {(success) in
-      if success {
+    let operations = OperationGroup(call:self, operations:[.sendInitialMetadata(metadata)])
+    {(operationGroup) in
+      if operationGroup.success {
         print("call successful")
       } else {
         return
@@ -291,13 +278,13 @@ public class Call {
 
   // receive initial metadata from a streaming connection
   private func receiveInitialMetadata() throws -> Void {
-    let operation_receiveInitialMetadata = Operation_ReceiveInitialMetadata()
-    let operations = OperationGroup(call:self, operations:[operation_receiveInitialMetadata])
-    {(success) in
-      if success {
-        let initialMetadata = operation_receiveInitialMetadata.metadata()
-        for j in 0..<initialMetadata.count() {
-          print("Received initial metadata -> " + initialMetadata.key(index:j) + " : " + initialMetadata.value(index:j))
+    let operations = OperationGroup(call:self, operations:[.receiveInitialMetadata])
+    {(operationGroup) in
+      if operationGroup.success {
+        if let initialMetadata = operationGroup.receivedInitialMetadata() {
+          for j in 0..<initialMetadata.count() {
+            print("Received initial metadata -> " + initialMetadata.key(index:j) + " : " + initialMetadata.value(index:j))
+          }
         }
       }
     }
@@ -306,12 +293,10 @@ public class Call {
 
   // receive status from a streaming connection
   private func receiveStatus() throws -> Void {
-    let operation_receiveStatus = Operation_ReceiveStatusOnClient()
-    let operations = OperationGroup(call:self,
-                                    operations:[operation_receiveStatus])
-    {(success) in
-      if success {
-        print("status = \(operation_receiveStatus.status()), \(operation_receiveStatus.statusDetails())")
+    let operations = OperationGroup(call:self, operations:[.receiveStatusOnClient])
+    {(operationGroup) in
+      if operationGroup.success {
+        print("status = \(operationGroup.receivedStatusCode()), \(operationGroup.receivedStatusMessage())")
       }
     }
     try self.perform(operations)
@@ -319,10 +304,9 @@ public class Call {
 
   // close a streaming connection
   public func close(completion:@escaping (() -> Void)) throws -> Void {
-    let operation_sendCloseFromClient = Operation_SendCloseFromClient()
-    let operations = OperationGroup(call:self, operations:[operation_sendCloseFromClient])
-    {(success) in
-      if success {
+    let operations = OperationGroup(call:self, operations:[.sendCloseFromClient])
+    {(operationGroup) in
+      if operationGroup.success {
         completion()
       }
     }

+ 2 - 1
Packages/gRPC/Sources/CompletionQueue.swift

@@ -119,7 +119,8 @@ internal class CompletionQueue {
           if let operationGroup = self.operationGroups[tag] {
             // call the operation group completion handler
             do {
-              try operationGroup.completion(event.success == 1)
+              operationGroup.success = (event.success == 1)
+              try operationGroup.completion(operationGroup)
             } catch (let callError) {
               print("grpc error: \(callError)")
             }

+ 30 - 41
Packages/gRPC/Sources/Handler.swift

@@ -101,16 +101,14 @@ public class Handler {
   /// - Returns: a tuple containing status codes and a message (if available)
   public func receiveMessage(initialMetadata: Metadata,
                              completion:@escaping ((Data?) throws -> Void)) throws -> Void {
-    let operation_sendInitialMetadata = Operation_SendInitialMetadata(metadata:initialMetadata);
-    let operation_receiveMessage = Operation_ReceiveMessage()
     let operations = OperationGroup(
       call:call,
       operations:[
-        operation_sendInitialMetadata,
-        operation_receiveMessage])
-    {(success) in
-      if (success) {
-        try completion(operation_receiveMessage.message()!.data())
+        .sendInitialMetadata(initialMetadata),
+        .receiveMessage])
+    {(operationGroup) in
+      if operationGroup.success {
+        try completion(operationGroup.receivedMessage()?.data())
       } else {
         try completion(nil)
       }
@@ -124,20 +122,15 @@ public class Handler {
   /// - Returns: a tuple containing status codes
   public func sendResponse(message: Data,
                            trailingMetadata: Metadata) throws -> Void {
-    let operation_receiveCloseOnServer = Operation_ReceiveCloseOnServer();
-    let operation_sendStatusFromServer = Operation_SendStatusFromServer(status:0,
-                                                                        statusDetails:"OK",
-                                                                        metadata:trailingMetadata)
     let messageBuffer = ByteBuffer(data:message)
-    let operation_sendMessage = Operation_SendMessage(message:messageBuffer)
     let operations = OperationGroup(
       call:call,
       operations:[
-        operation_receiveCloseOnServer,
-        operation_sendStatusFromServer,
-        operation_sendMessage])
-    {(success) in
-      if success {
+        .receiveCloseOnServer,
+        .sendStatusFromServer(0, "OK", trailingMetadata),
+        .sendMessage(messageBuffer)])
+    {(operationGroup) in
+      if operationGroup.success {
         self.shutdown()
       }
     }
@@ -152,10 +145,10 @@ public class Handler {
   /// Send initial metadata in response to a connection
   public func sendMetadata(initialMetadata: Metadata,
                            completion:@escaping (() throws -> Void)) throws -> Void {
-    let operation_sendInitialMetadata = Operation_SendInitialMetadata(metadata:initialMetadata);
-    let operations = OperationGroup(call:call, operations:[operation_sendInitialMetadata])
-    {(success) in
-      if success {
+    let operations = OperationGroup(call:call,
+                                    operations:[.sendInitialMetadata(initialMetadata)])
+    {(operationGroup) in
+      if operationGroup.success {
         try completion()
       } else {
         try completion()
@@ -168,12 +161,10 @@ public class Handler {
   ///
   /// - Returns: a tuple containing status codes and a message (if available)
   public func receiveMessage(completion:(@escaping (Data?) throws -> Void)) throws -> Void {
-    let operation_receiveMessage = Operation_ReceiveMessage()
-    let operations = OperationGroup(call:call, operations:[operation_receiveMessage])
-    {(success) in
-      if success {
-        print("server receiveMessage complete")
-        if let message = operation_receiveMessage.message() {
+    let operations = OperationGroup(call:call, operations:[.receiveMessage])
+    {(operationGroup) in
+      if operationGroup.success {
+        if let message = operationGroup.receivedMessage() {
           try completion(message.data())
         } else {
           try completion(nil)
@@ -191,10 +182,10 @@ public class Handler {
   /// - Returns: a tuple containing status codes
   public func sendResponse(message: Data,
                            completion: @escaping () throws -> Void) throws -> Void {
-    let operation_sendMessage = Operation_SendMessage(message:ByteBuffer(data:message))
-    let operations = OperationGroup(call:call, operations:[operation_sendMessage])
-    {(success) in
-      if success {
+    let operations = OperationGroup(call:call,
+                                    operations:[.sendMessage(ByteBuffer(data:message))])
+    {(operationGroup) in
+      if operationGroup.success {
         try completion()
       }
     }
@@ -203,10 +194,10 @@ public class Handler {
 
   /// Recognize when the client has closed a request
   public func receiveClose(completion: @escaping () throws -> Void) throws -> Void {
-    let operation_receiveClose = Operation_ReceiveCloseOnServer()
-    let operations = OperationGroup(call:call, operations:[operation_receiveClose])
-    {(success) in
-      if success {
+    let operations = OperationGroup(call:call,
+                                    operations:[.receiveCloseOnServer])
+    {(operationGroup) in
+      if operationGroup.success {
         try completion()
       }
     }
@@ -216,12 +207,10 @@ public class Handler {
   /// Send final status to the client
   public func sendStatus(trailingMetadata: Metadata,
                          completion:@escaping (() -> Void)) throws -> Void {
-    let operation_sendStatusFromServer = Operation_SendStatusFromServer(status:0,
-                                                                        statusDetails:"OK",
-                                                                        metadata:trailingMetadata)
-    let operations = OperationGroup(call:call, operations:[operation_sendStatusFromServer])
-    {(success) in
-      if success {
+    let operations = OperationGroup(call:call,
+                                    operations:[.sendStatusFromServer(0, "OK", trailingMetadata)])
+    {(operationGroup) in
+      if operationGroup.success {
         completion()
       }
     }

+ 9 - 142
Packages/gRPC/Sources/Operation.swift

@@ -33,147 +33,14 @@
 #if SWIFT_PACKAGE
   import CgRPC
 #endif
-import Foundation // for String.Encoding
 
-/// Abstract representation of gRPC Operations
-class Operation {
-
-  /// Pointer to underlying C representation
-  var underlyingObserver: UnsafeMutableRawPointer
-
-  /// Initializes an Operation Observer
-  ///
-  /// - Parameter observer: the underlying C representation
-  init(underlyingObserver: UnsafeMutableRawPointer) {
-    self.underlyingObserver = underlyingObserver
-  }
-
-  deinit {
-    cgrpc_observer_destroy(underlyingObserver);
-  }
-}
-
-/// SendInitialMetadata operation
-class Operation_SendInitialMetadata : Operation {
-
-  /// Initializes an Operation Observer
-  ///
-  /// - Parameter metadata: the initial metadata to send
-  init(metadata:Metadata) {
-    super.init(underlyingObserver:cgrpc_observer_create_send_initial_metadata(metadata.underlyingArray))
-  }
-}
-
-/// SendMessage operation
-class Operation_SendMessage : Operation {
-
-  /// Initializes an Operation Observer
-  ///
-  /// - Parameter message: the message to send
-  init(message:ByteBuffer) {
-    super.init(underlyingObserver:cgrpc_observer_create_send_message())
-    cgrpc_observer_send_message_set_message(underlyingObserver, message.underlyingByteBuffer);
-  }
-}
-
-/// SendCloseFromClient operation
-class Operation_SendCloseFromClient : Operation {
-
-  /// Initializes an Operation Observer
-  init() {
-    super.init(underlyingObserver:cgrpc_observer_create_send_close_from_client())
-  }
-}
-
-/// SendStatusFrom Server operation
-class Operation_SendStatusFromServer : Operation {
-
-  /// Initializes an Operation Observer
-  ///
-  /// - Parameter status: the status code to send with the response
-  /// - Parameter statusDetails: the status message to send with the response
-  /// - Parameter metadata: the trailing metadata to send with the response
-  init(status:Int,
-       statusDetails:String,
-       metadata:Metadata) {
-    super.init(underlyingObserver:cgrpc_observer_create_send_status_from_server(metadata.underlyingArray))
-    cgrpc_observer_send_status_from_server_set_status(underlyingObserver, Int32(status));
-    cgrpc_observer_send_status_from_server_set_status_details(underlyingObserver, statusDetails);
-  }
-}
-
-/// ReceiveInitialMetadata operation
-class Operation_ReceiveInitialMetadata : Operation {
-
-  /// Initializes an Operation Observer
-  init() {
-    super.init(underlyingObserver:cgrpc_observer_create_recv_initial_metadata())
-  }
-
-  /// Gets the initial metadata that was received
-  ///
-  /// - Returns: metadata
-  func metadata() -> Metadata {
-    return Metadata(underlyingArray:cgrpc_observer_recv_initial_metadata_get_metadata(underlyingObserver));
-  }
-}
-
-/// ReceiveMessage operation
-class Operation_ReceiveMessage : Operation {
-
-  /// Initializes an Operation Observer
-  init() {
-    super.init(underlyingObserver:cgrpc_observer_create_recv_message())
-  }
-
-  /// Gets the message that was received
-  ///
-  /// - Returns: message
-  func message() -> ByteBuffer? {
-    if let b = cgrpc_observer_recv_message_get_message(underlyingObserver) {
-      return ByteBuffer(underlyingByteBuffer:b)
-    } else {
-      return nil
-    }
-  }
-}
-
-/// ReceiveStatusOnClient operation
-class Operation_ReceiveStatusOnClient : Operation {
-
-  /// Initializes an Operation Observer
-  init() {
-    super.init(underlyingObserver:cgrpc_observer_create_recv_status_on_client())
-  }
-
-  /// Gets the trailing metadata that was received
-  ///
-  /// - Returns: metadata
-  func metadata() -> Metadata {
-    return Metadata(underlyingArray:cgrpc_observer_recv_status_on_client_get_metadata(underlyingObserver));
-  }
-
-  /// Gets the status code that was received
-  ///
-  /// - Returns: status code
-  func status() -> Int {
-    return cgrpc_observer_recv_status_on_client_get_status(underlyingObserver);
-  }
-
-  /// Gets the status message that was received
-  ///
-  /// - Returns: status message
-  func statusDetails() -> String {
-    return String(cString:cgrpc_observer_recv_status_on_client_get_status_details(underlyingObserver),
-                  encoding:String.Encoding.utf8)!
-  }
-}
-
-/// ReceiveCloseOnServer operation
-class Operation_ReceiveCloseOnServer : Operation {
-
-  /// Initializes an Operation Observer
-  init() {
-    super.init(underlyingObserver:cgrpc_observer_create_recv_close_on_server())
-  }
+enum Operation {
+  case sendInitialMetadata(Metadata)
+  case sendMessage(ByteBuffer)
+  case sendCloseFromClient
+  case sendStatusFromServer(Int, String, Metadata)
+  case receiveInitialMetadata
+  case receiveMessage
+  case receiveStatusOnClient
+  case receiveCloseOnServer
 }

+ 146 - 16
Packages/gRPC/Sources/OperationGroup.swift

@@ -44,45 +44,175 @@ private class OperationGroupTagLock {
 }
 
 /// A collection of gRPC operations
-class OperationGroup {
+internal class OperationGroup {
 
   /// Used to generate unique tags for OperationGroups
-  static var nextTag : Int64 = 1
+  private static var nextTag : Int64 = 1
 
-  /// Automatically-assigned tag that is used with the completion queue.
-  var tag : Int64
+  /// Automatically-assigned tag that is used by the completion queue that watches this group.
+  internal var tag : Int64
 
   /// The call associated with the operation group. Retained while the operations are running.
-  var call : Call
+  private var call : Call
 
-  /// An array of operation objects that are passed into the initializer
-  var operationsArray : [Operation]?
+  /// An array of operation objects that are passed into the initializer.
+  private var operations : [Operation]
+
+  /// An array of observers used to watch the operation
+  private var underlyingObservers : [UnsafeMutableRawPointer] = []
 
   /// Pointer to underlying C representation
-  var underlyingOperations : UnsafeMutableRawPointer
+  internal var underlyingOperations : UnsafeMutableRawPointer?
 
   /// Completion handler that is called when the group completes
-  var completion : ((Bool) throws -> Void)
+  internal var completion : ((OperationGroup) throws -> Void)
+
+  /// Indicates that the OperationGroup completed successfully
+  internal var success : Bool = false
+
+  /// Creates the underlying observer needed to run an operation
+  ///
+  /// - Parameter: operation: the operation to observe
+  /// - Returns: the observer
+  private func underlyingObserverForOperation(operation: Operation) -> UnsafeMutableRawPointer {
+    var underlyingObserver : UnsafeMutableRawPointer
+    switch operation {
+    case .sendInitialMetadata(let metadata):
+      underlyingObserver = cgrpc_observer_create_send_initial_metadata(metadata.underlyingArray)!
+    case .sendMessage(let message):
+      underlyingObserver = cgrpc_observer_create_send_message()!
+      cgrpc_observer_send_message_set_message(underlyingObserver, message.underlyingByteBuffer)
+    case .sendCloseFromClient:
+      underlyingObserver = cgrpc_observer_create_send_close_from_client()!
+    case .sendStatusFromServer(let statusCode, let statusMessage, let metadata):
+      underlyingObserver = cgrpc_observer_create_send_status_from_server(metadata.underlyingArray)!
+      cgrpc_observer_send_status_from_server_set_status(underlyingObserver, Int32(statusCode))
+      cgrpc_observer_send_status_from_server_set_status_details(underlyingObserver, statusMessage)
+    case .receiveInitialMetadata:
+      underlyingObserver = cgrpc_observer_create_recv_initial_metadata()!
+    case .receiveMessage:
+      underlyingObserver = cgrpc_observer_create_recv_message()!
+    case .receiveStatusOnClient:
+      underlyingObserver = cgrpc_observer_create_recv_status_on_client()!
+    case .receiveCloseOnServer:
+      underlyingObserver = cgrpc_observer_create_recv_close_on_server()!
+    }
+    return underlyingObserver
+  }
 
   /// Initializes an OperationGroup representation
   ///
   /// - Parameter operations: an array of operations
   init(call: Call,
        operations: [Operation],
-       completion: @escaping ((Bool) throws -> Void)) {
+       completion: @escaping ((OperationGroup) throws -> Void)) {
     self.call = call
-    self.operationsArray = operations
-    self.underlyingOperations = cgrpc_operations_create()
-    cgrpc_operations_reserve_space_for_operations(self.underlyingOperations, Int32(operations.count))
-    for operation in operations {
-      cgrpc_operations_add_operation(self.underlyingOperations, operation.underlyingObserver)
-    }
+    self.operations = operations
     self.completion = completion
+    // set tag
     let mutex = OperationGroupTagLock.sharedInstance.mutex
     mutex.lock()
     self.tag = OperationGroup.nextTag
     OperationGroup.nextTag += 1
     mutex.unlock()
+    // create observers
+    for operation in operations {
+      self.underlyingObservers.append(self.underlyingObserverForOperation(operation: operation))
+    }
+    // create operations
+    self.underlyingOperations = cgrpc_operations_create()
+    cgrpc_operations_reserve_space_for_operations(self.underlyingOperations, Int32(operations.count))
+    for underlyingObserver in underlyingObservers {
+      cgrpc_operations_add_operation(self.underlyingOperations, underlyingObserver)
+    }
+  }
+
+  deinit {
+    for underlyingObserver in underlyingObservers {
+      cgrpc_observer_destroy(underlyingObserver);
+    }
+    cgrpc_operations_destroy(underlyingOperations);
+  }
+
+  /// WARNING: The following assumes that at most one operation of each type is in the group.
+  
+  /// Gets the message that was received
+  ///
+  /// - Returns: message
+  internal func receivedMessage() -> ByteBuffer? {
+    for (i, operation) in operations.enumerated() {
+      switch (operation) {
+      case .receiveMessage:
+        if let b = cgrpc_observer_recv_message_get_message(underlyingObservers[i]) {
+          return ByteBuffer(underlyingByteBuffer:b)
+        } else {
+          return nil
+        }
+      default: continue
+      }
+    }
+    return nil
+  }
+
+  /// Gets the initial metadata that was received
+  ///
+  /// - Returns: metadata
+  internal func receivedInitialMetadata() -> Metadata? {
+    for (i, operation) in operations.enumerated() {
+      switch (operation) {
+      case .receiveInitialMetadata:
+        return Metadata(underlyingArray:cgrpc_observer_recv_initial_metadata_get_metadata(underlyingObservers[i]));
+      default:
+        continue
+      }
+    }
+    return nil
+  }
+
+  /// Gets the status code that was received
+  ///
+  /// - Returns: status code
+  internal func receivedStatusCode() -> Int? {
+    for (i, operation) in operations.enumerated() {
+      switch (operation) {
+      case .receiveStatusOnClient:
+        return cgrpc_observer_recv_status_on_client_get_status(underlyingObservers[i])
+      default:
+        continue
+      }
+    }
+    return nil
+  }
+
+  /// Gets the status message that was received
+  ///
+  /// - Returns: status message
+  internal func receivedStatusMessage() -> String? {
+    for (i, operation) in operations.enumerated() {
+      switch (operation) {
+      case .receiveStatusOnClient:
+        return String(cString:cgrpc_observer_recv_status_on_client_get_status_details(underlyingObservers[i]),
+                      encoding:String.Encoding.utf8)!
+      default:
+        continue
+      }
+    }
+    return nil
+  }
+
+  /// Gets the trailing metadata that was received
+  ///
+  /// - Returns: metadata
+  internal func receivedTrailingMetadata() -> Metadata? {
+    for (i, operation) in operations.enumerated() {
+      switch (operation) {
+      case .receiveStatusOnClient:
+        return Metadata(underlyingArray:cgrpc_observer_recv_status_on_client_get_metadata(underlyingObservers[i]));
+      default:
+        continue
+      }
+    }
+    return nil
   }
 }