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 }