You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
152 lines
2.9 KiB
152 lines
2.9 KiB
// Package tgerr implements helpers for error handling. |
|
package tgerr |
|
|
|
import ( |
|
"fmt" |
|
"strconv" |
|
"strings" |
|
|
|
"github.com/go-faster/errors" |
|
|
|
"github.com/gotd/td/internal/ascii" |
|
) |
|
|
|
// Error represents RPC error returned as result to request. |
|
type Error struct { |
|
Code int // 420 |
|
Message string // FLOOD_WAIT_3 |
|
Type string // FLOOD_WAIT |
|
Argument int // 3 |
|
} |
|
|
|
// New creates new *Error from code and message, extracting argument |
|
// and type. |
|
func New(code int, msg string) *Error { |
|
e := &Error{ |
|
Code: code, |
|
Message: msg, |
|
} |
|
e.extractArgument() |
|
return e |
|
} |
|
|
|
// IsType reports whether error has type t. |
|
func (e *Error) IsType(t string) bool { |
|
if e == nil { |
|
return false |
|
} |
|
return e.Type == t |
|
} |
|
|
|
// IsCode reports whether error Code is equal to code. |
|
func (e *Error) IsCode(code int) bool { |
|
if e == nil { |
|
return false |
|
} |
|
return e.Code == code |
|
} |
|
|
|
// IsOneOf returns true if error type is in tt. |
|
func (e *Error) IsOneOf(tt ...string) bool { |
|
if e == nil { |
|
return false |
|
} |
|
for _, t := range tt { |
|
if e.IsType(t) { |
|
return true |
|
} |
|
} |
|
return false |
|
} |
|
|
|
// IsCodeOneOf returns true if error code is one of codes. |
|
func (e *Error) IsCodeOneOf(codes ...int) bool { |
|
if e == nil { |
|
return false |
|
} |
|
for _, code := range codes { |
|
if e.IsCode(code) { |
|
return true |
|
} |
|
} |
|
return false |
|
} |
|
|
|
// extractArgument extracts Type and Argument from Message. |
|
func (e *Error) extractArgument() { |
|
if e.Message == "" { |
|
return |
|
} |
|
|
|
// Defaulting Type to Message. |
|
e.Type = e.Message |
|
|
|
// Splitting by underscore. |
|
parts := strings.Split(e.Message, "_") |
|
if len(parts) < 2 { |
|
return |
|
} |
|
|
|
var nonDigit []string |
|
Parts: |
|
for _, part := range parts { |
|
for _, r := range part { |
|
if ascii.IsDigit(r) { |
|
continue |
|
} |
|
|
|
// Found non-digit part, skipping. |
|
nonDigit = append(nonDigit, part) |
|
continue Parts |
|
} |
|
|
|
// Found digit-only part, using as argument. |
|
argument, err := strconv.Atoi(part) |
|
if err != nil { |
|
// Should be unreachable. |
|
return |
|
} |
|
e.Argument = argument |
|
} |
|
|
|
e.Type = strings.Join(nonDigit, "_") |
|
} |
|
|
|
func (e *Error) Error() string { |
|
if e.Type != e.Message { |
|
return fmt.Sprintf("rpc error code %d: %s (%d)", e.Code, e.Type, e.Argument) |
|
} |
|
return fmt.Sprintf("rpc error code %d: %s", e.Code, e.Message) |
|
} |
|
|
|
// AsType returns *Error from err if rpc error type is t. |
|
func AsType(err error, t string) (rpcErr *Error, ok bool) { |
|
if errors.As(err, &rpcErr) && rpcErr.Type == t { |
|
return rpcErr, true |
|
} |
|
return nil, false |
|
} |
|
|
|
// As extracts *Error from err if possible. |
|
func As(err error) (rpcErr *Error, ok bool) { |
|
if errors.As(err, &rpcErr) { |
|
return rpcErr, true |
|
} |
|
return nil, false |
|
} |
|
|
|
// Is returns true if err type is t. |
|
func Is(err error, tt ...string) bool { |
|
if rpcErr, ok := As(err); ok { |
|
return rpcErr.IsOneOf(tt...) |
|
} |
|
return false |
|
} |
|
|
|
// IsCode returns true of error code is as provided. |
|
func IsCode(err error, code ...int) bool { |
|
if rpcErr, ok := As(err); ok { |
|
return rpcErr.IsCodeOneOf(code...) |
|
} |
|
return false |
|
}
|
|
|