blob: bcba92c7b325faf6bb19af8b64832842a527f614 [file] [log] [blame]
// Copyright 2015 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.
package verror
import (
"fmt"
"v.io/v23/vdl"
)
func init() {
// TODO(bprosnitz) Remove this old-style registration.
// We must register the error conversion functions between vdl.WireError and
// the standard error interface with the vdl package. This allows the vdl
// package to have minimal dependencies.
vdl.RegisterNativeError(WireToNative, WireFromNative)
// New-style error registration.
vdl.RegisterNative(ErrorToNative, ErrorFromNative)
}
// TODO(toddw): rename Error{To,From}Native to Wire{To,From}Native, after we've
// switched to the new vdl Encoder/Decoder, and the old functions are no longer
// used.
// ErrorToNative converts from the wire to native representation of errors.
func ErrorToNative(wire *vdl.WireError, native *error) error {
if wire == nil {
*native = nil
return nil
}
e := new(E)
*native = e
return WireToNative(*wire, e)
}
// ErrorFromNative converts from the native to wire representation of errors.
func ErrorFromNative(wire **vdl.WireError, native error) error {
if native == nil {
*wire = nil
return nil
}
return WireFromNative(*wire, native)
}
// FromWire is a convenience for generated code to convert wire errors into
// native errors.
func FromWire(wire *vdl.WireError) error {
var native error
if err := ErrorToNative(wire, &native); err != nil {
native = err
}
return native
}
// WireToNative converts from vdl.WireError to verror.E, which
// implements the standard go error interface.
//
// TODO(toddw): Remove this function after the switch to the new vdl
// Encoder/Decoder is complete.
func WireToNative(wire vdl.WireError, native *E) error {
*native = E{
ID: ID(wire.Id),
Action: retryToAction(wire.RetryCode),
Msg: wire.Msg,
}
for _, p := range wire.ParamList {
var pNative interface{}
if err := vdl.Convert(&pNative, p); err != nil {
// It's questionable what to do if the conversion fails, or similarly if
// the conversion ends up with a *vdl.Value, rather than a native Go
// value.
//
// At the moment, for both cases we plug the *vdl.Value or conversion
// error into the native params. The idea is that this will still be more
// useful to the user, since they'll still have the error Id and Action.
//
// TODO(toddw): Consider whether there is a better strategy.
pNative = err
}
native.ParamList = append(native.ParamList, pNative)
}
return nil
}
// WireFromNative converts from the standard go error interface to
// verror.E, and then to vdl.WireError.
//
// TODO(toddw): Remove this function after the switch to the new vdl
// Encoder/Decoder is complete.
func WireFromNative(wire *vdl.WireError, native error) error {
e := ExplicitConvert(ErrUnknown, "", "", "", native)
*wire = vdl.WireError{
Id: string(ErrorID(e)),
RetryCode: retryFromAction(Action(e)),
Msg: e.Error(),
}
for _, p := range params(e) {
var pWire *vdl.Value
if err := vdl.Convert(&pWire, p); err != nil {
// It's questionable what to do here if the conversion fails, similarly to
// the conversion failure above in WireToNative.
//
// TODO(toddw): Consider whether there is a better strategy.
pWire = vdl.StringValue(nil, err.Error())
}
wire.ParamList = append(wire.ParamList, pWire)
}
return nil
}
func retryToAction(retry vdl.WireRetryCode) ActionCode {
switch retry {
case vdl.WireRetryCodeNoRetry:
return NoRetry
case vdl.WireRetryCodeRetryConnection:
return RetryConnection
case vdl.WireRetryCodeRetryRefetch:
return RetryRefetch
case vdl.WireRetryCodeRetryBackoff:
return RetryBackoff
}
// Backoff to no retry by default.
return NoRetry
}
func retryFromAction(action ActionCode) vdl.WireRetryCode {
switch action.RetryAction() {
case NoRetry:
return vdl.WireRetryCodeNoRetry
case RetryConnection:
return vdl.WireRetryCodeRetryConnection
case RetryRefetch:
return vdl.WireRetryCodeRetryRefetch
case RetryBackoff:
return vdl.WireRetryCodeRetryBackoff
}
// Backoff to no retry by default.
return vdl.WireRetryCodeNoRetry
}
// VDLRead implements the logic to read x from dec.
//
// Unlike regular VDLRead implementations, this handles the case where the
// decoder contains a nil value, to make code generation simpler.
func VDLRead(dec vdl.Decoder, x *error) error {
if err := dec.StartValue(); err != nil {
return err
}
if dec.IsNil() {
if (dec.StackDepth() == 1 || dec.IsAny()) && !vdl.Compatible(vdl.ErrorType, dec.Type()) {
return fmt.Errorf("incompatible error, from %v", dec.Type())
}
*x = nil
return dec.FinishValue()
}
dec.IgnoreNextStartValue()
var wire vdl.WireError
if err := wire.VDLRead(dec); err != nil {
return err
}
nativePtr := new(E)
if err := WireToNative(wire, nativePtr); err != nil {
return err
}
*x = nativePtr
return nil
}
// VDLWrite implements the logic to write x to enc.
//
// Unlike regular VDLWrite implementations, this handles the case where x
// contains a nil value, to make code generation simpler.
func VDLWrite(enc vdl.Encoder, x error) error {
if x == nil {
return enc.NilValue(vdl.ErrorType)
}
var wire vdl.WireError
if err := WireFromNative(&wire, x); err != nil {
return err
}
return wire.VDLWrite(enc)
}