github.com/openimsdk/tools@v0.0.49/errs/coderr.go (about)

     1  // Copyright © 2023 OpenIM. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package errs
    16  
    17  import (
    18  	"strconv"
    19  	"strings"
    20  
    21  	"github.com/pkg/errors"
    22  )
    23  
    24  var DefaultCodeRelation = newCodeRelation()
    25  
    26  type CodeError interface {
    27  	Code() int
    28  	Msg() string
    29  	Detail() string
    30  	WithDetail(detail string) CodeError
    31  	Error
    32  }
    33  
    34  func NewCodeError(code int, msg string) CodeError {
    35  	return &codeError{
    36  		code: code,
    37  		msg:  msg,
    38  	}
    39  }
    40  
    41  type codeError struct {
    42  	code   int
    43  	msg    string
    44  	detail string
    45  }
    46  
    47  func (e *codeError) Code() int {
    48  	return e.code
    49  }
    50  
    51  func (e *codeError) Msg() string {
    52  	return e.msg
    53  }
    54  
    55  func (e *codeError) Detail() string {
    56  	return e.detail
    57  }
    58  
    59  func (e *codeError) WithDetail(detail string) CodeError {
    60  	var d string
    61  	if e.detail == "" {
    62  		d = detail
    63  	} else {
    64  		d = e.detail + ", " + detail
    65  	}
    66  	return &codeError{
    67  		code:   e.code,
    68  		msg:    e.msg,
    69  		detail: d,
    70  	}
    71  }
    72  
    73  func (e *codeError) Wrap() error {
    74  	return Wrap(e)
    75  }
    76  
    77  func (e *codeError) WrapMsg(msg string, kv ...any) error {
    78  	return WrapMsg(e, msg, kv...)
    79  }
    80  
    81  func (e *codeError) Is(err error) bool {
    82  	codeErr, ok := Unwrap(err).(CodeError)
    83  	if !ok {
    84  		if err == nil && e == nil {
    85  			return true
    86  		}
    87  		return false
    88  	}
    89  	if e == nil {
    90  		return false
    91  	}
    92  	code := codeErr.Code()
    93  	if e.code == code {
    94  		return true
    95  	}
    96  	return DefaultCodeRelation.Is(e.code, code)
    97  }
    98  
    99  const initialCapacity = 3
   100  
   101  func (e *codeError) Error() string {
   102  	v := make([]string, 0, initialCapacity)
   103  	v = append(v, strconv.Itoa(e.code), e.msg)
   104  
   105  	if e.detail != "" {
   106  		v = append(v, e.detail)
   107  	}
   108  
   109  	return strings.Join(v, " ")
   110  }
   111  
   112  func Unwrap(err error) error {
   113  	for err != nil {
   114  		unwrap, ok := err.(interface {
   115  			Unwrap() error
   116  		})
   117  		if !ok {
   118  			break
   119  		}
   120  		err = unwrap.Unwrap()
   121  	}
   122  	return err
   123  }
   124  
   125  func Wrap(err error) error {
   126  	return errors.WithStack(err)
   127  }
   128  
   129  func WrapMsg(err error, msg string, kv ...any) error {
   130  	if err == nil {
   131  		return nil
   132  	}
   133  	withMessage := errors.WithMessage(err, toString(msg, kv))
   134  	return errors.WithStack(withMessage)
   135  }
   136  
   137  type CodeRelation interface {
   138  	Add(codes ...int) error
   139  	Is(parent, child int) bool
   140  }
   141  
   142  func newCodeRelation() CodeRelation {
   143  	return &codeRelation{m: make(map[int]map[int]struct{})}
   144  }
   145  
   146  type codeRelation struct {
   147  	m map[int]map[int]struct{}
   148  }
   149  
   150  const minimumCodesLength = 2
   151  
   152  func (r *codeRelation) Add(codes ...int) error {
   153  	if len(codes) < minimumCodesLength {
   154  		return New("codes length must be greater than 2", "codes", codes).Wrap()
   155  	}
   156  	for i := 1; i < len(codes); i++ {
   157  		parent := codes[i-1]
   158  		s, ok := r.m[parent]
   159  		if !ok {
   160  			s = make(map[int]struct{})
   161  			r.m[parent] = s
   162  		}
   163  		for _, code := range codes[i:] {
   164  			s[code] = struct{}{}
   165  		}
   166  	}
   167  	return nil
   168  }
   169  
   170  func (r *codeRelation) Is(parent, child int) bool {
   171  	if parent == child {
   172  		return true
   173  	}
   174  	s, ok := r.m[parent]
   175  	if !ok {
   176  		return false
   177  	}
   178  	_, ok = s[child]
   179  	return ok
   180  }