...

Source file src/edge-infra.dev/pkg/lib/errors/contextual_error.go

Documentation: edge-infra.dev/pkg/lib/errors

     1  // Package errors provides common custom Error types.
     2  package errors
     3  
     4  import (
     5  	"fmt"
     6  	"runtime"
     7  	"strings"
     8  )
     9  
    10  // ContextualError is a custom error that can wrap an existing error and provides
    11  // additional context based on the function that created the error, if created
    12  // via New()
    13  type ContextualError struct {
    14  	Err     error
    15  	Context string
    16  	Message string
    17  }
    18  
    19  // New creates a ContextualError using the provided message and wrapped Error.
    20  // It also attempts to determine additional context automatically based on the
    21  // file + function that is calling New().  If the context cannot be determined,
    22  // it gracefully degrades into a regular wrapped Error.
    23  //
    24  // Error context is in the form:
    25  //
    26  // $package:$file#$line
    27  //
    28  // edge-infra.dev/pkg/lib/errors.TestContextualError:pkg/errors/contextual_errors_test.go#12
    29  func New(msg string, e error) *ContextualError {
    30  	err := &ContextualError{Message: msg, Err: e}
    31  
    32  	// attempt to find out information about the caller
    33  	pc, file, line, ok := runtime.Caller(1)
    34  	details := runtime.FuncForPC(pc)
    35  	// if we discovered it, add it as context
    36  	if ok && details != nil {
    37  		name := details.Name()
    38  		// e.g., contextual_error.go:New#20
    39  		err.Context = fmt.Sprintf("%s:%s#%d", name, file, line)
    40  	}
    41  	return err
    42  }
    43  
    44  // Error builds the error string based on the ContextualError state
    45  func (c *ContextualError) Error() string {
    46  	if c.Message == "" {
    47  		c.Message = "failed."
    48  	}
    49  
    50  	str := c.Message
    51  	// add punctuation if not present
    52  	if !(strings.HasSuffix(c.Message, ".") ||
    53  		strings.HasSuffix(c.Message, "!") ||
    54  		strings.HasSuffix(c.Message, "?")) {
    55  		str = fmt.Sprintf("%s.", str)
    56  	}
    57  
    58  	// add wrapped error text if present
    59  	if c.Err != nil {
    60  		str = fmt.Sprintf("%s error: %v", str, c.Err)
    61  	}
    62  
    63  	// add caller context if present
    64  	if c.Context != "" {
    65  		str = fmt.Sprintf("%s: %s", c.Context, str)
    66  	}
    67  
    68  	return str
    69  }
    70  
    71  func (c *ContextualError) Unwrap() error { return c.Err }
    72  
    73  // Wrap creates a ContextualError without a message
    74  func Wrap(e error) *ContextualError {
    75  	return New("", e)
    76  }
    77  

View as plain text