github.com/vicanso/hes@v0.7.0/http_errors.go (about)

     1  package hes
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"net/http"
     8  	"runtime"
     9  	"strings"
    10  	"sync"
    11  )
    12  
    13  const (
    14  	defaultStatusCode = http.StatusBadRequest
    15  )
    16  
    17  var callerEnabled = false
    18  
    19  type (
    20  	// Error http error
    21  	Error struct {
    22  		lock *sync.RWMutex
    23  		// http status code
    24  		StatusCode int `json:"statusCode,omitempty"`
    25  		// error code
    26  		Code string `json:"code,omitempty"`
    27  		// category
    28  		Category string `json:"category,omitempty"`
    29  		// sub category
    30  		SubCategory string `json:"subCategory,omitempty"`
    31  		// title
    32  		Title string `json:"title,omitempty"`
    33  		// message
    34  		Message string `json:"message,omitempty"`
    35  		// exception error
    36  		Exception bool `json:"exception,omitempty"`
    37  		// original error
    38  		Err error `json:"-"`
    39  		// File caller file
    40  		File string `json:"file,omitempty"`
    41  		// Line caller line
    42  		Line int `json:"line,omitempty"`
    43  		// extra info for error
    44  		Extra map[string]interface{} `json:"extra,omitempty"`
    45  		// sub errors
    46  		Errs []*Error `json:"errs,omitempty"`
    47  	}
    48  	FileConvertor func(file string) string
    49  )
    50  
    51  // EnableCaller enable caller
    52  func EnableCaller(enabled bool) {
    53  	callerEnabled = enabled
    54  }
    55  
    56  // fileConvertor file convertor
    57  var fileConvertor FileConvertor
    58  
    59  // SetFileConvertor set file convertor
    60  func SetFileConvertor(fn FileConvertor) {
    61  	fileConvertor = fn
    62  }
    63  
    64  // Error error interface
    65  func (e *Error) Error() string {
    66  	str := fmt.Sprintf("message=%s", e.Message)
    67  
    68  	if e.Code != "" {
    69  		str = fmt.Sprintf("code=%s, %s", e.Code, str)
    70  	}
    71  
    72  	if e.Category != "" {
    73  		str = fmt.Sprintf("category=%s, %s", e.Category, str)
    74  	}
    75  
    76  	if e.StatusCode != 0 {
    77  		str = fmt.Sprintf("statusCode=%d, %s", e.StatusCode, str)
    78  	}
    79  
    80  	if e.File != "" {
    81  		str = fmt.Sprintf("file=%s, line=%d, %s", e.File, e.Line, str)
    82  	}
    83  	if len(e.Errs) != 0 {
    84  		arr := make([]string, len(e.Errs))
    85  		for index, err := range e.Errs {
    86  			arr[index] = err.Error()
    87  		}
    88  		str = fmt.Sprintf("%s, errs:(%s)", str, strings.Join(arr, ","))
    89  	}
    90  
    91  	return str
    92  }
    93  
    94  // Format error format
    95  func (e *Error) Format(s fmt.State, verb rune) {
    96  	switch verb {
    97  	default:
    98  		fallthrough
    99  	case 's':
   100  		_, _ = io.WriteString(s, e.Error())
   101  	case 'q':
   102  		fmt.Fprintf(s, "%q", e.Message)
   103  	}
   104  }
   105  
   106  // SetCaller set info of caller
   107  func (e *Error) SetCaller(skip int) {
   108  	if e.lock != nil {
   109  		e.lock.Lock()
   110  		defer e.lock.Unlock()
   111  	}
   112  	_, file, line, _ := runtime.Caller(skip)
   113  	if fileConvertor != nil {
   114  		file = fileConvertor(file)
   115  	}
   116  	e.File = file
   117  	e.Line = line
   118  }
   119  
   120  // ToJSON error to json
   121  func (e *Error) ToJSON() []byte {
   122  	buf, _ := json.Marshal(e)
   123  	return buf
   124  }
   125  
   126  // CloneWithMessage clone error and update message
   127  func (e *Error) CloneWithMessage(message string) *Error {
   128  	clone := e.Clone()
   129  	clone.Message = message
   130  	return clone
   131  }
   132  
   133  // IsEmpty check the error list is empty
   134  func (e *Error) IsEmpty() bool {
   135  	if e.lock != nil {
   136  		e.lock.RLock()
   137  		defer e.lock.RUnlock()
   138  	}
   139  	return len(e.Errs) == 0
   140  }
   141  
   142  // IsNotEmpty check the error list is not empty
   143  func (e *Error) IsNotEmpty() bool {
   144  	// is empty有判断锁,因此不需要判断
   145  	return !e.IsEmpty()
   146  }
   147  
   148  // AddExtra add extra value to error
   149  func (e *Error) AddExtra(key string, value interface{}) {
   150  	if e.lock != nil {
   151  		e.lock.Lock()
   152  		defer e.lock.Unlock()
   153  	}
   154  	if e.Extra == nil {
   155  		e.Extra = make(map[string]interface{})
   156  	}
   157  	e.Extra[key] = value
   158  }
   159  
   160  // Exists return true if it already exists
   161  func (e *Error) exists(he *Error) bool {
   162  	for _, item := range e.Errs {
   163  		if he.Title == item.Title &&
   164  			he.Message == item.Message &&
   165  			he.Category == item.Category {
   166  			return true
   167  		}
   168  	}
   169  	return false
   170  }
   171  func (e *Error) add(errs ...error) {
   172  	if len(e.Errs) == 0 {
   173  		e.Errs = make([]*Error, 0)
   174  	}
   175  	for _, err := range errs {
   176  		if err == nil {
   177  			continue
   178  		}
   179  		he := Wrap(err)
   180  		// 如果包括子错误,则直接添加子错误列表
   181  		if he.IsNotEmpty() {
   182  			for _, err := range he.Errs {
   183  				e.add(err)
   184  			}
   185  			continue
   186  		}
   187  		// 判断是否已存在相同的error
   188  		// 如果已有相同error,则不添加
   189  		if e.exists(he) {
   190  			continue
   191  		}
   192  
   193  		e.Errs = append(e.Errs, he)
   194  	}
   195  }
   196  
   197  // Add add error to error list
   198  func (e *Error) Add(errs ...error) {
   199  	if len(errs) == 0 {
   200  		return
   201  	}
   202  	if e.lock != nil {
   203  		e.lock.Lock()
   204  		defer e.lock.Unlock()
   205  	}
   206  	e.add(errs...)
   207  }
   208  
   209  // Clone clone error
   210  func (e *Error) Clone() *Error {
   211  	he := new(Error)
   212  	*he = *e
   213  	if he.lock != nil {
   214  		he.lock = &sync.RWMutex{}
   215  	}
   216  	return he
   217  }
   218  
   219  func newError(message string, statusCode, skip int, category ...string) *Error {
   220  	he := &Error{
   221  		Message:    message,
   222  		StatusCode: statusCode,
   223  	}
   224  	if len(category) != 0 {
   225  		he.Category = category[0]
   226  	}
   227  	if callerEnabled {
   228  		he.SetCaller(skip)
   229  	}
   230  	return he
   231  }
   232  
   233  // New create a http error
   234  func New(message string, category ...string) *Error {
   235  	he := newError(message, defaultStatusCode, 3, category...)
   236  	return he
   237  }
   238  
   239  // NewMutex create a http error with mutex
   240  func NewMutex(message string, category ...string) *Error {
   241  	he := New(message, category...)
   242  	he.lock = &sync.RWMutex{}
   243  	return he
   244  }
   245  
   246  // NewWithStatusCode create a http error with status code
   247  func NewWithStatusCode(message string, statusCode int, category ...string) *Error {
   248  	he := newError(message, statusCode, 3, category...)
   249  	return he
   250  }
   251  
   252  // NewWithError create a http error with error
   253  func NewWithError(err error) *Error {
   254  	he := newError(err.Error(), defaultStatusCode, 3)
   255  	he.Err = err
   256  	return he
   257  }
   258  
   259  // NewWithErrorStatusCode create a http error with error and status code
   260  func NewWithErrorStatusCode(err error, statusCode int) *Error {
   261  	he := newError(err.Error(), statusCode, 3)
   262  	he.Err = err
   263  	return he
   264  }
   265  
   266  // NewWithCaller create a http error with caller
   267  func NewWithCaller(message string) *Error {
   268  	he := &Error{
   269  		Message:    message,
   270  		StatusCode: defaultStatusCode,
   271  	}
   272  	he.SetCaller(2)
   273  	return he
   274  }
   275  
   276  // NewWithException create a http error and set exception to true
   277  func NewWithException(message string) *Error {
   278  	he := newError(message, defaultStatusCode, 3)
   279  	he.Exception = true
   280  	return he
   281  }
   282  
   283  // IsError check the error whether or not hes error
   284  func IsError(err error) bool {
   285  	_, ok := err.(*Error)
   286  	return ok
   287  }
   288  
   289  // Wrap wrap error
   290  func Wrap(err error) *Error {
   291  	he, ok := err.(*Error)
   292  	if ok {
   293  		return he.Clone()
   294  	}
   295  	he = newError(err.Error(), defaultStatusCode, 3)
   296  	he.Err = err
   297  	return he
   298  }