| // Copyright 2016 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| import Foundation |
| |
| public typealias GranterHandle = Int32 |
| |
| public enum RpcClientOption { |
| // TODO(zinman): Uncomment when VDL generation is baked in to build process |
| // case AllowedServersPolicy(polices:[BlessingPattern]) |
| case RetryTimeout(timeout:NSTimeInterval) |
| case Granter(granter:GranterHandle) |
| } |
| |
| public struct Client { |
| internal let defaultContext:Context |
| |
| internal init(defaultContext:Context) { |
| self.defaultContext = defaultContext |
| } |
| |
| internal func ctxHandle(ctx:Context?) -> ContextHandle { |
| return (ctx ?? defaultContext).handle |
| } |
| |
| public func startCall(ctx:Context?=nil, name:String, method:String, args:[AnyObject]?=nil, returnArgsLength:Int, |
| skipServerAuth:Bool=false) -> Promise<ClientCall> { |
| let vomArgs = SwiftByteArrayArray(length: 0, data: nil) |
| |
| let (asyncId, handleP) = Client.outstandingHandles.newPromise() |
| swift_io_v_impl_google_rpc_ClientImpl_nativeStartCallAsync( |
| self.ctxHandle(ctx).goHandle, |
| name.toGo(), |
| method.toGo(), |
| vomArgs, |
| skipServerAuth.toGo(), |
| asyncId, |
| { asyncId, handle in Client.callDidSucceed(asyncId, handle: handle) }, |
| { asyncId, err in Client.callDidFail(asyncId, err: err) }) |
| return handleP.then { handle throws in |
| return try ClientCall( |
| ctxHandle: self.ctxHandle(ctx), callHandle: handle, returnArgsLength: returnArgsLength) |
| } |
| } |
| |
| private static let outstandingHandles = GoPromises<GoClientCallHandle>(timeout: nil) |
| |
| public func call(ctx:Context?=nil, name:String, method:String, args:[AnyObject]?=nil, returnArgsLength:Int, |
| skipServerAuth:Bool=false) -> Promise<[AnyObject]?> { |
| return startCall( |
| ctx, |
| name: name, |
| method: method, |
| args: args, |
| returnArgsLength: returnArgsLength, |
| skipServerAuth: skipServerAuth) |
| .then { call throws -> Promise<[AnyObject]?> in |
| return try call.finish() |
| } |
| } |
| |
| internal static func callDidSucceed(asyncId:AsyncCallbackIdentifier, handle:_GoHandle) { |
| if let p = Client.outstandingHandles.getAndDeleteRef(asyncId) { |
| RunOnMain { |
| do { |
| try p.resolve(handle) |
| } catch let e { |
| log.warning("Unable to resolve asyncCall start with handle \(handle): \(e)") |
| } |
| } |
| } |
| } |
| |
| internal static func callDidFail(asyncId:AsyncCallbackIdentifier, err:SwiftVError) { |
| let verr = err.toSwift() |
| if let p = Client.outstandingHandles.getAndDeleteRef(asyncId) { |
| RunOnMain { |
| do { |
| try p.reject(verr) |
| } catch let e { |
| log.warning("Unable to reject asyncCall start with err \(verr): \(e)") |
| } |
| } |
| } |
| } |
| |
| public func close(ctx:Context?=nil) { |
| swift_io_v_impl_google_rpc_ClientImpl_nativeClose(ctxHandle(ctx).goHandle) |
| } |
| } |
| |
| public enum ClientCallErrors: ErrorType { |
| case NilHandlerOnInit |
| } |
| |
| public class ClientCall { |
| internal let ctxHandle:ContextHandle |
| internal let callHandle:GoClientCallHandle |
| internal let returnArgsLength:Int |
| |
| internal init(ctxHandle:ContextHandle, callHandle:GoClientCallHandle, returnArgsLength:Int) throws { |
| self.ctxHandle = ctxHandle |
| self.callHandle = callHandle |
| self.returnArgsLength = returnArgsLength |
| guard callHandle != 0 else { |
| throw ClientCallErrors.NilHandlerOnInit |
| } |
| } |
| |
| deinit { |
| if callHandle != 0 { |
| swift_io_v_impl_google_rpc_ClientCallImpl_nativeFinalize(callHandle) |
| } |
| } |
| |
| public func closeSend() throws { |
| try SwiftVError.catchAndThrowError { errPtr in |
| swift_io_v_impl_google_rpc_ClientCallImpl_nativeCloseSend(ctxHandle.goHandle, callHandle, errPtr) |
| } |
| } |
| |
| private static let outstandingFinishes = GoPromises<[AnyObject]?>(timeout: nil) |
| public func finish() throws -> Promise<[AnyObject]?> { |
| let (asyncId, p) = ClientCall.outstandingFinishes.newPromise() |
| swift_io_v_impl_google_rpc_ClientCallImpl_nativeFinishAsync( |
| ctxHandle.goHandle, callHandle, returnArgsLength.toGo(), asyncId, |
| { asyncId, byteArrayArray in ClientCall.finishDidSucceed(asyncId, byteArrayArray: byteArrayArray) }, |
| { asyncId, err in ClientCall.finishDidFail(asyncId, err: err) }) |
| return p |
| } |
| |
| internal static func finishDidSucceed(asyncId:AsyncCallbackIdentifier, byteArrayArray:SwiftByteArrayArray) { |
| guard let p = ClientCall.outstandingFinishes.getAndDeleteRef(asyncId) else { |
| log.warning("Couldn't find associated promise for finish succeeding on asyncId \(asyncId)") |
| // Deallocate associated bytes malloc'd in Go |
| byteArrayArray.dealloc() |
| return |
| } |
| |
| // VOM decode in background to let Go get free'd up and to not block main |
| RunInBackground { |
| // Deallocate associated bytes malloc'd in Go |
| defer { byteArrayArray.dealloc() } |
| |
| do { |
| // TODO Decode via VOM |
| // log.debug("Got back byteArrayArray \(byteArrayArray) and data \(byteArrayArray.data)") |
| // var i = 0 |
| // for byteArray in byteArrayArray { |
| // log.debug("Byte array \(i): \(byteArray)") |
| // i += 1 |
| // } |
| } catch let e { |
| do { try p.reject(e) } catch {} |
| return |
| } |
| |
| do { |
| try p.resolve(nil) |
| } catch let e { |
| log.warning("Unable to resolve finish promise: \(e)") |
| } |
| } |
| } |
| |
| internal static func finishDidFail(asyncId:AsyncCallbackIdentifier, err:SwiftVError) { |
| let verr = err.toSwift() |
| if let p = ClientCall.outstandingFinishes.getAndDeleteRef(asyncId) { |
| RunOnMain { |
| do { |
| try p.reject(verr) |
| } catch let e { |
| log.warning("Unable to reject finish with err \(verr): \(e)") |
| } |
| } |
| } |
| } |
| |
| // public func remoteBlessings() -> (blessings:[String], cryptoBlessings:[Blessings]) { |
| // fatalError("Unimplemented") |
| // } |
| } |