blob: 8491381974e23bb48e436e21bd54a2db1d25f622 [file] [log] [blame]
// 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
/// VError is the primary vehicle for errors in Vanadium. Errors can be defined and customized
/// in VDL which will get auto-generated to static instantiations of VError. Runtime errors
/// should contain all the context-sensitive information such as a stacktrace, messages, etc.
public struct VError : ErrorType, Equatable {
public let identity:String
public let action:ErrorAction
public let msg:String?
public let stacktrace:String?
}
/// Equality for VError is determined SOLELY by comparing identifiers. Identifiers are required
/// to be unique in VDL. Equality is defined this way as the primary use case is to compare
/// runtime VErrors against VDL-generated static errors. For example:
///
/// do { throw VError(identity: "syncbase.Error", action: ErrorAction.NoRetry,
/// msg: "some msg", stacktrace: nil)
/// } catch let e as VError {
/// switch e {
/// case Syncbase.Error: print("Comparing only identity allowed this")
/// }
/// }
public func ==(lhs: VError, rhs: VError) -> Bool {
return lhs.identity == rhs.identity
}
/// SwiftVError is the C struct that the go bridge uses to transfer an error to Swift.
internal extension SwiftVError {
internal func isEmpty() -> Bool { return identity == nil || identity.memory == 0 }
/// Helper to run a go-bridge method that might fill out this struct on error. In that scenario
/// we translate that into a Swift-based error and throw it.
/// VError's static _delegate slot is the actual function that throws a given converted VError
/// into its apropriate enum-based error. That error will be generated from the VDL, which is why
/// we need this hook here. A default handler just throws the VError itself, which is of ErrorType.
internal static func catchAndThrowError<T>(@noescape block: UnsafeMutablePointer<SwiftVError>->T) throws -> T {
let ptr = UnsafeMutablePointer<SwiftVError>.alloc(1)
defer { ptr.dealloc(1) }
ptr.initialize(SwiftVError())
let ret = block(ptr)
let verr = ptr.memory
if !verr.isEmpty() {
let err = verr.toSwift()
throw err
}
return ret
}
/// Convert the C Struct into a Swift-based VError struct.
/// Go allocates the underlying strings, and swift must take control of them and free them when done
internal func toSwift() -> VError {
return VError(
identity: String.fromCStringNoCopy(identity, freeWhenDone: true)!,
action: ErrorAction.init(rawValue: UInt32(actionCode)) ?? ErrorAction.NoRetry,
msg: String.fromCStringNoCopy(msg, freeWhenDone: true),
stacktrace: String.fromCStringNoCopy(stacktrace, freeWhenDone: true))
}
}
public enum ErrorAction: UInt32 {
case NoRetry = 0 // Do not retry.
case RetryConnection = 1 // Renew high-level connection/context.
case RetryRefetch = 2 // Refetch and retry (e.g., out of date HTTP ETag)
case RetryBackoff = 3 // Backoff and retry a finite number of times.
}