errors.go (4000B)
1 // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 // Use of this source code is governed by a MIT style 3 // license that can be found in the LICENSE file. 4 5 package gin 6 7 import ( 8 "fmt" 9 "reflect" 10 "strings" 11 12 "github.com/gin-gonic/gin/internal/json" 13 ) 14 15 // ErrorType is an unsigned 64-bit error code as defined in the gin spec. 16 type ErrorType uint64 17 18 const ( 19 // ErrorTypeBind is used when Context.Bind() fails. 20 ErrorTypeBind ErrorType = 1 << 63 21 // ErrorTypeRender is used when Context.Render() fails. 22 ErrorTypeRender ErrorType = 1 << 62 23 // ErrorTypePrivate indicates a private error. 24 ErrorTypePrivate ErrorType = 1 << 0 25 // ErrorTypePublic indicates a public error. 26 ErrorTypePublic ErrorType = 1 << 1 27 // ErrorTypeAny indicates any other error. 28 ErrorTypeAny ErrorType = 1<<64 - 1 29 // ErrorTypeNu indicates any other error. 30 ErrorTypeNu = 2 31 ) 32 33 // Error represents a error's specification. 34 type Error struct { 35 Err error 36 Type ErrorType 37 Meta any 38 } 39 40 type errorMsgs []*Error 41 42 var _ error = (*Error)(nil) 43 44 // SetType sets the error's type. 45 func (msg *Error) SetType(flags ErrorType) *Error { 46 msg.Type = flags 47 return msg 48 } 49 50 // SetMeta sets the error's meta data. 51 func (msg *Error) SetMeta(data any) *Error { 52 msg.Meta = data 53 return msg 54 } 55 56 // JSON creates a properly formatted JSON 57 func (msg *Error) JSON() any { 58 jsonData := H{} 59 if msg.Meta != nil { 60 value := reflect.ValueOf(msg.Meta) 61 switch value.Kind() { 62 case reflect.Struct: 63 return msg.Meta 64 case reflect.Map: 65 for _, key := range value.MapKeys() { 66 jsonData[key.String()] = value.MapIndex(key).Interface() 67 } 68 default: 69 jsonData["meta"] = msg.Meta 70 } 71 } 72 if _, ok := jsonData["error"]; !ok { 73 jsonData["error"] = msg.Error() 74 } 75 return jsonData 76 } 77 78 // MarshalJSON implements the json.Marshaller interface. 79 func (msg *Error) MarshalJSON() ([]byte, error) { 80 return json.Marshal(msg.JSON()) 81 } 82 83 // Error implements the error interface. 84 func (msg Error) Error() string { 85 return msg.Err.Error() 86 } 87 88 // IsType judges one error. 89 func (msg *Error) IsType(flags ErrorType) bool { 90 return (msg.Type & flags) > 0 91 } 92 93 // Unwrap returns the wrapped error, to allow interoperability with errors.Is(), errors.As() and errors.Unwrap() 94 func (msg *Error) Unwrap() error { 95 return msg.Err 96 } 97 98 // ByType returns a readonly copy filtered the byte. 99 // ie ByType(gin.ErrorTypePublic) returns a slice of errors with type=ErrorTypePublic. 100 func (a errorMsgs) ByType(typ ErrorType) errorMsgs { 101 if len(a) == 0 { 102 return nil 103 } 104 if typ == ErrorTypeAny { 105 return a 106 } 107 var result errorMsgs 108 for _, msg := range a { 109 if msg.IsType(typ) { 110 result = append(result, msg) 111 } 112 } 113 return result 114 } 115 116 // Last returns the last error in the slice. It returns nil if the array is empty. 117 // Shortcut for errors[len(errors)-1]. 118 func (a errorMsgs) Last() *Error { 119 if length := len(a); length > 0 { 120 return a[length-1] 121 } 122 return nil 123 } 124 125 // Errors returns an array with all the error messages. 126 // Example: 127 // 128 // c.Error(errors.New("first")) 129 // c.Error(errors.New("second")) 130 // c.Error(errors.New("third")) 131 // c.Errors.Errors() // == []string{"first", "second", "third"} 132 func (a errorMsgs) Errors() []string { 133 if len(a) == 0 { 134 return nil 135 } 136 errorStrings := make([]string, len(a)) 137 for i, err := range a { 138 errorStrings[i] = err.Error() 139 } 140 return errorStrings 141 } 142 143 func (a errorMsgs) JSON() any { 144 switch length := len(a); length { 145 case 0: 146 return nil 147 case 1: 148 return a.Last().JSON() 149 default: 150 jsonData := make([]any, length) 151 for i, err := range a { 152 jsonData[i] = err.JSON() 153 } 154 return jsonData 155 } 156 } 157 158 // MarshalJSON implements the json.Marshaller interface. 159 func (a errorMsgs) MarshalJSON() ([]byte, error) { 160 return json.Marshal(a.JSON()) 161 } 162 163 func (a errorMsgs) String() string { 164 if len(a) == 0 { 165 return "" 166 } 167 var buffer strings.Builder 168 for i, msg := range a { 169 fmt.Fprintf(&buffer, "Error #%02d: %s\n", i+1, msg.Err) 170 if msg.Meta != nil { 171 fmt.Fprintf(&buffer, " Meta: %v\n", msg.Meta) 172 } 173 } 174 return buffer.String() 175 }