blob: e2f1728eac57c1b523839b02e60f426134a12770 [file] [log] [blame]
Christian Lavoieafc6d8f2011-02-20 02:39:19 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20package thrift
21
Jens Geyer527b6d92014-11-30 15:07:18 +010022import (
23 "errors"
Yuxuan 'fishy' Wang892b6732022-08-01 12:47:12 -070024 "reflect"
Jens Geyer527b6d92014-11-30 15:07:18 +010025)
26
Jens Geyer0e87c462013-06-18 22:25:07 +020027// Generic Thrift exception
28type TException interface {
29 error
Yuxuan 'fishy' Wangd8312302020-12-22 09:53:58 -080030
31 TExceptionType() TExceptionType
Christian Lavoieafc6d8f2011-02-20 02:39:19 +000032}
Jens Geyer527b6d92014-11-30 15:07:18 +010033
Konrad Grochowskidae6d3c2014-12-01 11:26:07 +010034// Prepends additional information to an error without losing the Thrift exception interface
Jens Geyer527b6d92014-11-30 15:07:18 +010035func PrependError(prepend string, err error) error {
Yuxuan 'fishy' Wangd8312302020-12-22 09:53:58 -080036 msg := prepend + err.Error()
37
Yuxuan 'fishy' Wange27e82c2021-01-19 11:07:58 -080038 var te TException
39 if errors.As(err, &te) {
Yuxuan 'fishy' Wangd8312302020-12-22 09:53:58 -080040 switch te.TExceptionType() {
41 case TExceptionTypeTransport:
42 if t, ok := err.(TTransportException); ok {
Yuxuan 'fishy' Wang0e68e8c2021-01-19 09:14:36 -080043 return prependTTransportException(prepend, t)
Yuxuan 'fishy' Wangd8312302020-12-22 09:53:58 -080044 }
45 case TExceptionTypeProtocol:
46 if t, ok := err.(TProtocolException); ok {
Yuxuan 'fishy' Wang0e68e8c2021-01-19 09:14:36 -080047 return prependTProtocolException(prepend, t)
Yuxuan 'fishy' Wangd8312302020-12-22 09:53:58 -080048 }
49 case TExceptionTypeApplication:
Yuxuan 'fishy' Wange27e82c2021-01-19 11:07:58 -080050 var t TApplicationException
51 if errors.As(err, &t) {
Yuxuan 'fishy' Wangd8312302020-12-22 09:53:58 -080052 return NewTApplicationException(t.TypeId(), msg)
53 }
54 }
55
56 return wrappedTException{
Yuxuan 'fishy' Wang0e68e8c2021-01-19 09:14:36 -080057 err: err,
58 msg: msg,
Yuxuan 'fishy' Wangd8312302020-12-22 09:53:58 -080059 tExceptionType: te.TExceptionType(),
60 }
Jens Geyer527b6d92014-11-30 15:07:18 +010061 }
62
Yuxuan 'fishy' Wangd8312302020-12-22 09:53:58 -080063 return errors.New(msg)
Jens Geyer527b6d92014-11-30 15:07:18 +010064}
Yuxuan 'fishy' Wangd8312302020-12-22 09:53:58 -080065
66// TExceptionType is an enum type to categorize different "subclasses" of TExceptions.
67type TExceptionType byte
68
69// TExceptionType values
70const (
71 TExceptionTypeUnknown TExceptionType = iota
72 TExceptionTypeCompiled // TExceptions defined in thrift files and generated by thrift compiler
73 TExceptionTypeApplication // TApplicationExceptions
74 TExceptionTypeProtocol // TProtocolExceptions
75 TExceptionTypeTransport // TTransportExceptions
76)
77
78// WrapTException wraps an error into TException.
79//
80// If err is nil or already TException, it's returned as-is.
81// Otherwise it will be wraped into TException with TExceptionType() returning
82// TExceptionTypeUnknown, and Unwrap() returning the original error.
83func WrapTException(err error) TException {
84 if err == nil {
85 return nil
86 }
87
88 if te, ok := err.(TException); ok {
89 return te
90 }
91
92 return wrappedTException{
93 err: err,
Yuxuan 'fishy' Wang0e68e8c2021-01-19 09:14:36 -080094 msg: err.Error(),
Yuxuan 'fishy' Wangd8312302020-12-22 09:53:58 -080095 tExceptionType: TExceptionTypeUnknown,
96 }
97}
98
99type wrappedTException struct {
100 err error
Yuxuan 'fishy' Wang0e68e8c2021-01-19 09:14:36 -0800101 msg string
Yuxuan 'fishy' Wangd8312302020-12-22 09:53:58 -0800102 tExceptionType TExceptionType
103}
104
105func (w wrappedTException) Error() string {
Yuxuan 'fishy' Wang0e68e8c2021-01-19 09:14:36 -0800106 return w.msg
Yuxuan 'fishy' Wangd8312302020-12-22 09:53:58 -0800107}
108
109func (w wrappedTException) TExceptionType() TExceptionType {
110 return w.tExceptionType
111}
112
113func (w wrappedTException) Unwrap() error {
114 return w.err
115}
116
117var _ TException = wrappedTException{}
Yuxuan 'fishy' Wang892b6732022-08-01 12:47:12 -0700118
119// ExtractExceptionFromResult extracts exceptions defined in thrift IDL from
120// result TStruct used in TClient.Call.
121//
122// For a endpoint defined in thrift IDL like this:
123//
124// service MyService {
125// FooResponse foo(1: FooRequest request) throws (
126// 1: Exception1 error1,
127// 2: Exception2 error2,
128// )
129// }
130//
131// The thrift compiler generated go code for the result TStruct would be like:
132//
133// type MyServiceFooResult struct {
134// Success *FooResponse `thrift:"success,0" db:"success" json:"success,omitempty"`
135// Error1 *Exception1 `thrift:"error1,1" db:"error1" json:"error1,omitempty"`
136// Error2 *Exception2 `thrift:"error2,2" db:"error2" json:"error2,omitempty"`
137// }
138//
139// And this function extracts the first non-nil exception out of
140// *MyServiceFooResult.
141func ExtractExceptionFromResult(result TStruct) error {
142 v := reflect.Indirect(reflect.ValueOf(result))
143 if v.Kind() != reflect.Struct {
144 return nil
145 }
146 typ := v.Type()
147 for i := 0; i < v.NumField(); i++ {
148 if typ.Field(i).Name == "Success" {
149 continue
150 }
151 field := v.Field(i)
152 if field.IsZero() {
153 continue
154 }
155 tExc, ok := field.Interface().(TException)
156 if ok && tExc != nil && tExc.TExceptionType() == TExceptionTypeCompiled {
157 return tExc
158 }
159 }
160 return nil
161}