github.com/telepresenceio/telepresence/v2@v2.20.0-pro.6.0.20240517030216-236ea954e789/pkg/errcat/errors.go (about)

     1  package errcat
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/telepresenceio/telepresence/rpc/v2/common"
     8  )
     9  
    10  // The Category is used for categorizing errors so that we can know when
    11  // to point the user to the logs or not.
    12  type Category int
    13  
    14  type categorized struct {
    15  	error
    16  	category Category
    17  }
    18  
    19  const (
    20  	OK           = Category(iota)
    21  	User         // User made an error
    22  	Config       // Errors in config.yml, extensions, or kubeconfig
    23  	NoDaemonLogs // Other error generated in the CLI process, so no use pointing the user to logs
    24  	Unknown      // Something else. Consult the logs
    25  )
    26  
    27  // New creates a new categorized error based in its argument. The argument
    28  // can be an error or a string. If it isn't, it will be converted to a string
    29  // using its '%v' formatter.
    30  func (c Category) New(untypedErr any) error {
    31  	var err error
    32  	switch untypedErr := untypedErr.(type) {
    33  	case nil:
    34  		return nil
    35  	case error:
    36  		err = untypedErr
    37  	case string:
    38  		err = errors.New(untypedErr)
    39  	default:
    40  		err = fmt.Errorf("%v", untypedErr)
    41  	}
    42  	return &categorized{error: err, category: c}
    43  }
    44  
    45  // Newf creates a new categorized error based on a format string with arguments. The
    46  // error is created using fmt.Errorf() so using '%w' is relevant for error arguments.
    47  func (c Category) Newf(format string, a ...any) error {
    48  	return &categorized{error: fmt.Errorf(format, a...), category: c}
    49  }
    50  
    51  // Unwrap this categorized error.
    52  func (ce *categorized) Unwrap() error {
    53  	return ce.error
    54  }
    55  
    56  // GetCategory returns the error category for a categorized error, OK for nil, and
    57  // Unknown for other errors.
    58  func GetCategory(err error) Category {
    59  	if err == nil {
    60  		return OK
    61  	}
    62  	// Keep unwrapping until a category is found (or not)
    63  	for {
    64  		if ce, ok := err.(*categorized); ok {
    65  			return ce.category
    66  		}
    67  		if err = errors.Unwrap(err); err == nil {
    68  			return Unknown
    69  		}
    70  	}
    71  }
    72  
    73  func FromResult(r *common.Result) error {
    74  	if r == nil {
    75  		return nil
    76  	}
    77  	c := Category(r.ErrorCategory)
    78  	if c == OK {
    79  		return nil
    80  	}
    81  	return &categorized{error: errors.New(string(r.Data)), category: c}
    82  }
    83  
    84  func ToResult(err error) *common.Result {
    85  	r := &common.Result{}
    86  	if err != nil {
    87  		r.Data = []byte(err.Error())
    88  		r.ErrorCategory = common.Result_ErrorCategory(GetCategory(err))
    89  	}
    90  	return r
    91  }