github.com/haraldrudell/parl@v0.4.176/perrors/parlerror.go (about) 1 /* 2 © 2021–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package perrors 7 8 import ( 9 "sync" 10 11 "github.com/haraldrudell/parl/perrors/errorglue" 12 ) 13 14 const ( 15 peNil = "<nil>" 16 ) 17 18 // ParlError is a thread-safe error container that can optionally 19 // send errors non-blocking on a channel. 20 // ParlError is a 2018 construct and is deprecated in favor of parl.NBChan[error]. 21 // NBChan is both a channel and a store, providing the consumer additional freedoms. 22 // 23 // Depreacted: use [github.com/haraldrudell/parl.ErrSlice] error container 24 type ParlError struct { 25 errLock sync.RWMutex 26 err error // inside lock 27 28 errorglue.SendNb // non-blocking channel for sending errors Send() Shutdown() 29 } 30 31 var _ error = &ParlError{} // ParlError behaves like an error 32 var _ errorglue.ErrorStore = &ParlError{} // ParlError is an error store 33 34 /* 35 NewParlError provides a thread-safe error container that can optionally 36 send incoming errors non-blocking on a channel. 37 38 If a channel is not used, a zero-value works: 39 40 var err error116.ParlError 41 … 42 return err 43 44 When using a channel, The error channel is closed by Shutdown(): 45 46 errCh := make(chan error) 47 err := NewParlError(errCh) 48 … 49 err.Shutdown() 50 … 51 if err, ok := <- errCh; !ok { 52 // errs was shutdown 53 54 A shutdown ParlError is still usable, but will no longer send errors 55 */ 56 func NewParlError(errCh chan error) (pe *ParlError) { 57 p := ParlError{} 58 if errCh != nil { 59 p.SendNb = *errorglue.NewSendNb(errCh) 60 } 61 return &p 62 } 63 64 // AddError stores additional errors in the container. 65 // Thread-safe. Returns the current state. 66 // For a non-thread-safe version, use error116.Errp 67 func (pe *ParlError) AddError(err error) (err1 error) { 68 69 // if there is no error, no change 70 if err == nil { 71 return pe.GetError() 72 } 73 74 // update the container 75 err1 = pe.addError(err) 76 77 // send if we have a channel, never block 78 pe.Send(err) 79 80 return 81 } 82 83 // AddErrorProc stores additional errors in the container 84 // It is thread-safe and has a no-return-value signature. 85 // For a non-thread-safe version, use error116.Errp 86 func (pe *ParlError) AddErrorProc(err error) { 87 pe.AddError(err) 88 } 89 90 // GetError returns the error value enclosed in ParlError. Thread-safe 91 func (pe *ParlError) GetError() (err error) { 92 pe.errLock.RLock() 93 defer pe.errLock.RUnlock() 94 95 return pe.err 96 } 97 98 // InvokeIfError invokes fn if the error store contains an error 99 func (pe *ParlError) InvokeIfError(fn func(err error)) { 100 if err := pe.GetError(); err != nil { 101 fn(err) 102 } 103 } 104 105 // Error() makes ParlError behave like an error. 106 // Error is thread-safe unlike in most other Error implementations. 107 // Because code will check if ParlError is nil, which it mostly isn’t, and then 108 // invoke .Error(), it may be that Error is invoked when the error field is nil. 109 // For those situations, return “<nil>” like fmt.Print might do 110 func (pe *ParlError) Error() string { 111 if err := pe.GetError(); err != nil { 112 return err.Error() 113 } 114 return peNil 115 } 116 117 func (pe *ParlError) addError(err error) (err1 error) { 118 pe.errLock.Lock() 119 defer pe.errLock.Unlock() 120 121 // determine the new value 122 if pe.err == nil { 123 err1 = err 124 } else { 125 err1 = AppendError(pe.err, err) 126 } 127 128 // store the new value 129 pe.err = err1 130 131 return 132 }