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 }