code.vegaprotocol.io/vega@v0.79.0/libs/errors/errors.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package errors
    17  
    18  import (
    19  	"fmt"
    20  	"strings"
    21  )
    22  
    23  var (
    24  	errSelfReference   = fmt.Errorf("<self reference>")
    25  	errParentReference = fmt.Errorf("<parent reference>")
    26  )
    27  
    28  type CumulatedErrors struct {
    29  	Errors []error
    30  }
    31  
    32  func NewCumulatedErrors() *CumulatedErrors {
    33  	return &CumulatedErrors{}
    34  }
    35  
    36  func (e *CumulatedErrors) Add(err error) {
    37  	// prevent adding this instance of cumulatedErrors to itself.
    38  	if err == e {
    39  		err = errSelfReference
    40  	} else if cerr, ok := err.(*CumulatedErrors); ok {
    41  		// nothing to add.
    42  		if !cerr.HasAny() {
    43  			return
    44  		}
    45  		// create a copy of the error we're adding
    46  		cpy := &CumulatedErrors{
    47  			Errors: append([]error{}, cerr.Errors...),
    48  		}
    49  		// remove any references to the parent from the error we're adding.
    50  		err = cpy.checkRef(e, errParentReference)
    51  	}
    52  	e.Errors = append(e.Errors, err)
    53  }
    54  
    55  // check recursively if a cumulated errors object contains a certain reference, and if so, replace with a placehold, simple error.
    56  // returns either itself (with the replaced references), or a replacement error.
    57  func (e *CumulatedErrors) checkRef(ref *CumulatedErrors, repl error) error {
    58  	if e == ref {
    59  		return repl
    60  	}
    61  	// recursively remove a given reference.
    62  	for i, subE := range e.Errors {
    63  		if subE == ref {
    64  			e.Errors[i] = repl
    65  		} else if cErr, ok := subE.(*CumulatedErrors); ok {
    66  			e.Errors[i] = cErr.checkRef(ref, repl)
    67  		}
    68  	}
    69  	return e
    70  }
    71  
    72  func (e *CumulatedErrors) HasAny() bool {
    73  	return len(e.Errors) > 0
    74  }
    75  
    76  func (e *CumulatedErrors) Error() string {
    77  	fmtErrors := make([]string, 0, len(e.Errors))
    78  	for _, err := range e.Errors {
    79  		fmtErrors = append(fmtErrors, err.Error())
    80  	}
    81  
    82  	return strings.Join(fmtErrors, ", also ")
    83  }