github.com/biogo/biogo@v1.0.4/errors/chain.go (about)

     1  // Copyright ©2013 The bíogo Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package errors
     6  
     7  import (
     8  	"sync"
     9  )
    10  
    11  // Chain is an error and layered error annotations.
    12  type Chain interface {
    13  	// The error behavior of a Chain is based on the last annotation applied.
    14  	error
    15  
    16  	// Cause returns the initial error in the Chain.
    17  	Cause() error
    18  
    19  	// Link adds an annotation layer to the Chain.
    20  	Link(error) Chain
    21  
    22  	// Last returns the Chain, or nil if the Chain is empty, and the most recent annotation.
    23  	Last() (Chain, error)
    24  }
    25  
    26  // Links is an optional interface used by the Errors function.
    27  type Links interface {
    28  	Errors() []error // Errors returns a flat list of errors in temporal order of annotation.
    29  }
    30  
    31  // NewChain returns a new Chain based on the provided error. If the error is a Chain it
    32  // is returned unaltered.
    33  func NewChain(err error) Chain {
    34  	if c, ok := err.(Chain); ok {
    35  		return c
    36  	}
    37  	return chain{m: new(sync.RWMutex), errors: []error{err}}
    38  }
    39  
    40  // Cause returns the initially identified cause of an error if the error is a Chain, or the error
    41  // itself if it is not.
    42  func Cause(err error) error {
    43  	if c, ok := err.(Chain); ok {
    44  		return c.Cause()
    45  	}
    46  	return err
    47  }
    48  
    49  // Link adds an annotation to an error, returning a Chain.
    50  func Link(err, annotation error) Chain { return NewChain(err).Link(annotation) }
    51  
    52  // Last returns the most recent annotation of an error and the remaining chain
    53  // after the annotation is removed or nil if no further errors remain. Last returns
    54  // a nil Chain if the error is not a Chain.
    55  func Last(err error) (Chain, error) {
    56  	if c, ok := err.(Chain); ok {
    57  		return c.Last()
    58  	}
    59  	return nil, err
    60  }
    61  
    62  // Errors returns a flat list of errors in temporal order of annotation. If the provided
    63  // error is not a Chain a single element slice of error is returned containing the error.
    64  // If the error implements Links, its Errors method is called and the result returned.
    65  func Errors(err error) []error {
    66  	if err == nil {
    67  		return nil
    68  	}
    69  	switch c := err.(type) {
    70  	case Links:
    71  		return c.Errors()
    72  	case Chain:
    73  		var errs []error
    74  		for c != nil {
    75  			c, err = c.Last()
    76  			errs = append(errs, err)
    77  		}
    78  		return reverse(errs)
    79  	default:
    80  		return []error{err}
    81  	}
    82  }
    83  
    84  func reverse(err []error) []error {
    85  	for i, j := 0, len(err)-1; i < j; i, j = i+1, j-1 {
    86  		err[i], err[j] = err[j], err[i]
    87  	}
    88  	return err
    89  }
    90  
    91  // chain is the basic implementation.
    92  type chain struct {
    93  	m      *sync.RWMutex
    94  	errors []error
    95  }
    96  
    97  func (c chain) Error() string {
    98  	c.m.RLock()
    99  	defer c.m.RUnlock()
   100  	if len(c.errors) > 0 {
   101  		return c.errors[len(c.errors)-1].Error()
   102  	}
   103  	return ""
   104  }
   105  func (c chain) Cause() error {
   106  	c.m.RLock()
   107  	defer c.m.RUnlock()
   108  	if len(c.errors) > 0 {
   109  		return c.errors[0]
   110  	}
   111  	return nil
   112  }
   113  func (c chain) Link(err error) Chain {
   114  	c.m.Lock()
   115  	defer c.m.Unlock()
   116  	c.errors = append(c.errors, err)
   117  	return c
   118  }
   119  func (c chain) Last() (Chain, error) {
   120  	c.m.RLock()
   121  	defer c.m.RUnlock()
   122  	switch len(c.errors) {
   123  	case 0:
   124  		return nil, nil
   125  	case 1:
   126  		return nil, c.errors[0]
   127  	default:
   128  		c.errors = c.errors[:len(c.errors)-1]
   129  		return c, c.errors[len(c.errors)-1]
   130  	}
   131  }
   132  func (c chain) Errors() []error {
   133  	c.m.RLock()
   134  	defer c.m.RUnlock()
   135  	return c.errors
   136  }