116 lines
3.1 KiB
Go
116 lines
3.1 KiB
Go
|
/*
|
||
|
Copyright 2017 Google Inc. All Rights Reserved.
|
||
|
|
||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
you may not use this file except in compliance with the License.
|
||
|
You may obtain a copy of the License at
|
||
|
|
||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
||
|
Unless required by applicable law or agreed to in writing, software
|
||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
See the License for the specific language governing permissions and
|
||
|
limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package spanner
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
|
||
|
"golang.org/x/net/context"
|
||
|
"google.golang.org/grpc"
|
||
|
"google.golang.org/grpc/codes"
|
||
|
"google.golang.org/grpc/metadata"
|
||
|
)
|
||
|
|
||
|
// Error is the structured error returned by Cloud Spanner client.
|
||
|
type Error struct {
|
||
|
// Code is the canonical error code for describing the nature of a
|
||
|
// particular error.
|
||
|
Code codes.Code
|
||
|
// Desc explains more details of the error.
|
||
|
Desc string
|
||
|
// trailers are the trailers returned in the response, if any.
|
||
|
trailers metadata.MD
|
||
|
}
|
||
|
|
||
|
// Error implements error.Error.
|
||
|
func (e *Error) Error() string {
|
||
|
if e == nil {
|
||
|
return fmt.Sprintf("spanner: OK")
|
||
|
}
|
||
|
return fmt.Sprintf("spanner: code = %q, desc = %q", e.Code, e.Desc)
|
||
|
}
|
||
|
|
||
|
// decorate decorates an existing spanner.Error with more information.
|
||
|
func (e *Error) decorate(info string) {
|
||
|
e.Desc = fmt.Sprintf("%v, %v", info, e.Desc)
|
||
|
}
|
||
|
|
||
|
// spannerErrorf generates a *spanner.Error with the given error code and
|
||
|
// description.
|
||
|
func spannerErrorf(ec codes.Code, format string, args ...interface{}) error {
|
||
|
return &Error{
|
||
|
Code: ec,
|
||
|
Desc: fmt.Sprintf(format, args...),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// toSpannerError converts general Go error to *spanner.Error.
|
||
|
func toSpannerError(err error) error {
|
||
|
return toSpannerErrorWithMetadata(err, nil)
|
||
|
}
|
||
|
|
||
|
// toSpannerErrorWithMetadata converts general Go error and grpc trailers to *spanner.Error.
|
||
|
// Note: modifies original error if trailers aren't nil
|
||
|
func toSpannerErrorWithMetadata(err error, trailers metadata.MD) error {
|
||
|
if err == nil {
|
||
|
return nil
|
||
|
}
|
||
|
if se, ok := err.(*Error); ok {
|
||
|
if trailers != nil {
|
||
|
se.trailers = metadata.Join(se.trailers, trailers)
|
||
|
}
|
||
|
return se
|
||
|
}
|
||
|
switch {
|
||
|
case err == context.DeadlineExceeded:
|
||
|
return &Error{codes.DeadlineExceeded, err.Error(), trailers}
|
||
|
case err == context.Canceled:
|
||
|
return &Error{codes.Canceled, err.Error(), trailers}
|
||
|
case grpc.Code(err) == codes.Unknown:
|
||
|
return &Error{codes.Unknown, err.Error(), trailers}
|
||
|
default:
|
||
|
return &Error{grpc.Code(err), grpc.ErrorDesc(err), trailers}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ErrCode extracts the canonical error code from a Go error.
|
||
|
func ErrCode(err error) codes.Code {
|
||
|
se, ok := toSpannerError(err).(*Error)
|
||
|
if !ok {
|
||
|
return codes.Unknown
|
||
|
}
|
||
|
return se.Code
|
||
|
}
|
||
|
|
||
|
// ErrDesc extracts the Cloud Spanner error description from a Go error.
|
||
|
func ErrDesc(err error) string {
|
||
|
se, ok := toSpannerError(err).(*Error)
|
||
|
if !ok {
|
||
|
return err.Error()
|
||
|
}
|
||
|
return se.Desc
|
||
|
}
|
||
|
|
||
|
// errTrailers extracts the grpc trailers if present from a Go error.
|
||
|
func errTrailers(err error) metadata.MD {
|
||
|
se, ok := err.(*Error)
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
return se.trailers
|
||
|
}
|