// Package errorsx contains error extensions. package errorsx import ( "errors" "github.com/ooni/probe-cli/v3/internal/netxlite/errorsx" ) // SafeErrWrapperBuilder contains a builder for ErrWrapper that // is safe, i.e., behaves correctly when the error is nil. type SafeErrWrapperBuilder struct { // Error is the error, if any Error error // Classifier is the local error to string classifier. When there is no // configured classifier we will use the generic classifier. Classifier func(err error) string // Operation is the operation that failed Operation string } // MaybeBuild builds a new ErrWrapper, if b.Error is not nil, and returns // a nil error value, instead, if b.Error is nil. func (b SafeErrWrapperBuilder) MaybeBuild() (err error) { if b.Error != nil { classifier := b.Classifier if classifier == nil { classifier = errorsx.ClassifyGenericError } err = &errorsx.ErrWrapper{ Failure: classifier(b.Error), Operation: toOperationString(b.Error, b.Operation), WrappedErr: b.Error, } } return } func toOperationString(err error, operation string) string { var errwrapper *errorsx.ErrWrapper if errors.As(err, &errwrapper) { // Basically, as explained in ErrWrapper docs, let's // keep the child major operation, if any. if errwrapper.Operation == errorsx.ConnectOperation { return errwrapper.Operation } if errwrapper.Operation == errorsx.HTTPRoundTripOperation { return errwrapper.Operation } if errwrapper.Operation == errorsx.ResolveOperation { return errwrapper.Operation } if errwrapper.Operation == errorsx.TLSHandshakeOperation { return errwrapper.Operation } if errwrapper.Operation == errorsx.QUICHandshakeOperation { return errwrapper.Operation } if errwrapper.Operation == "quic_handshake_start" { return errorsx.QUICHandshakeOperation } if errwrapper.Operation == "quic_handshake_done" { return errorsx.QUICHandshakeOperation } // FALLTHROUGH } return operation }