github.com/alkemics/goflow@v0.2.1/errors.go (about)

     1  package goflow
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"regexp"
     7  	"strconv"
     8  	"strings"
     9  )
    10  
    11  type Error struct {
    12  	Filename string
    13  
    14  	Err error
    15  }
    16  
    17  func (e Error) Error() string {
    18  	return fmt.Sprintf("%s: %s", e.Filename, e.Err.Error())
    19  }
    20  
    21  func (e Error) Unwrap() error {
    22  	return e.Err
    23  }
    24  
    25  func (e Error) Format(s fmt.State, verb rune) {
    26  	if verb != 'v' || (!s.Flag('+')) {
    27  		fmt.Fprint(s, e.Error())
    28  		return
    29  	}
    30  
    31  	errMsg := fmt.Sprintf("  %v", e.Err)
    32  
    33  	var multiErr MultiError
    34  	if errors.As(e.Err, &multiErr) {
    35  		flat := multiErr.flatten()
    36  		strs := make([]string, len(flat.Errs))
    37  		for i, err := range flat.Errs {
    38  			strs[i] = fmt.Sprintf("  %v", err)
    39  		}
    40  		errMsg = strings.Join(strs, "\n")
    41  	}
    42  
    43  	fmt.Fprintf(s, `%s:
    44  %v
    45  `,
    46  		e.Filename,
    47  		errMsg,
    48  	)
    49  }
    50  
    51  type MultiError struct {
    52  	Errs []error
    53  }
    54  
    55  func (e MultiError) Error() string {
    56  	if len(e.Errs) == 1 {
    57  		return e.Errs[0].Error()
    58  	}
    59  
    60  	strs := make([]string, len(e.Errs))
    61  	for i, err := range e.Errs {
    62  		strs[i] = err.Error()
    63  	}
    64  	return fmt.Sprintf("[%s]", strings.Join(strs, ", "))
    65  }
    66  
    67  func (e MultiError) Is(err error) bool {
    68  	for _, sub := range e.Errs {
    69  		if errors.Is(sub, err) {
    70  			return true
    71  		}
    72  	}
    73  	return false
    74  }
    75  
    76  func (e MultiError) As(target interface{}) bool {
    77  	for _, sub := range e.Errs {
    78  		if errors.As(sub, target) {
    79  			return true
    80  		}
    81  	}
    82  	return false
    83  }
    84  
    85  func (e MultiError) flatten() MultiError {
    86  	errs := make([]error, 0, len(e.Errs))
    87  	for _, err := range e.Errs {
    88  		var me MultiError
    89  		if errors.As(err, &me) {
    90  			errs = append(errs, me.flatten().Errs...)
    91  		} else {
    92  			errs = append(errs, err)
    93  		}
    94  	}
    95  	return MultiError{Errs: errs}
    96  }
    97  
    98  type GraphError struct {
    99  	Wrapper string
   100  	Err     error
   101  }
   102  
   103  func (e GraphError) Error() string {
   104  	return fmt.Sprintf("%v (%s)", e.Err, e.Wrapper)
   105  }
   106  
   107  func (e GraphError) Unwrap() error {
   108  	return e.Err
   109  }
   110  
   111  type NodeError struct {
   112  	ID      string
   113  	Wrapper string
   114  
   115  	Err error
   116  }
   117  
   118  func (e NodeError) Error() string {
   119  	return fmt.Sprintf("%s: %v (%s)", e.ID, e.Err, e.Wrapper)
   120  }
   121  
   122  func (e NodeError) Unwrap() error {
   123  	return e.Err
   124  }
   125  
   126  var yamlErrorRegex = regexp.MustCompile(`(?s)(?:yaml: )?(?:unmarshal errors:.)?line (\d+): (.*)`)
   127  
   128  type YAMLError struct {
   129  	Line int
   130  	Err  error
   131  }
   132  
   133  func (e YAMLError) Error() string {
   134  	return fmt.Sprintf("line %d: %v (yaml)", e.Line, e.Err.Error())
   135  }
   136  
   137  func (e YAMLError) Unwrap() error {
   138  	return e.Err
   139  }
   140  
   141  func ParseYAMLError(err error) error {
   142  	if err == nil {
   143  		return nil
   144  	}
   145  
   146  	match := yamlErrorRegex.FindStringSubmatch(err.Error())
   147  	if len(match) == 0 {
   148  		return err
   149  	}
   150  
   151  	line, _ := strconv.Atoi(match[1])
   152  	return YAMLError{
   153  		Line: line,
   154  		Err:  errors.New(strings.TrimSpace(match[2])),
   155  	}
   156  }