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.

153 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
}