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  }