mirror of https://github.com/pulumi/pulumi.git
106 lines
3.1 KiB
Go
106 lines
3.1 KiB
Go
// Copyright 2016-2023, Pulumi Corporation.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package result
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
|
|
)
|
|
|
|
type bailError struct {
|
|
err error
|
|
}
|
|
|
|
// A BailError represents an expected error or a graceful failure -- that is
|
|
// something which is not a bug but a normal (albeit unhappy-path) part of the
|
|
// program's execution. A BailError implements the Error interface but will
|
|
// prefix its error string with "BAIL: ", which if ever seen in user-facing
|
|
// messages indicates that a check for bailing was missed. It also does *not*
|
|
// implement Unwrap. To ascertain whether an error is a BailError, use the
|
|
// IsBail function.
|
|
func BailError(err error) error {
|
|
contract.Requiref(err != nil, "err", "must not be nil")
|
|
|
|
return &bailError{err: err}
|
|
}
|
|
|
|
func (b *bailError) Error() string {
|
|
return fmt.Sprintf("BAIL: %v", b.err)
|
|
}
|
|
|
|
// BailErrorf is a helper for BailError(fmt.Errorf(...)).
|
|
func BailErrorf(format string, args ...interface{}) error {
|
|
return BailError(fmt.Errorf(format, args...))
|
|
}
|
|
|
|
// FprintBailf writes a formatted string to the given writer and returns a BailError with the same message.
|
|
func FprintBailf(w io.Writer, msg string, args ...any) error {
|
|
msg = fmt.Sprintf(msg, args...)
|
|
fmt.Fprintln(w, msg)
|
|
return BailError(errors.New(msg))
|
|
}
|
|
|
|
// IsBail returns true if any error in the given error's tree is a BailError.
|
|
func IsBail(err error) bool {
|
|
if err == nil {
|
|
return false
|
|
}
|
|
|
|
var bail *bailError
|
|
ok := errors.As(err, &bail)
|
|
return ok
|
|
}
|
|
|
|
// MergeBails accepts a set of errors and returns a single error that is the
|
|
// result of merging them according to the following criteria:
|
|
//
|
|
// - If all the errors are nil, MergeBails returns nil.
|
|
// - If any of the errors is *not* a BailError, MergeBails returns a single
|
|
// error whose message is the concatenation of the messages of all the
|
|
// errors which are not bails (that is, if any error is unexpected, MergeBails
|
|
// will propagate it).
|
|
// - In the remaining case that all errors are either nil or BailErrors, MergeBails
|
|
// will return a single BailError whose message is the concatenation of the
|
|
// messages of all the BailErrors.
|
|
func MergeBails(errs ...error) error {
|
|
allNil := true
|
|
joinableErrs := []error{}
|
|
for _, err := range errs {
|
|
if err == nil {
|
|
continue
|
|
}
|
|
|
|
allNil = false
|
|
if IsBail(err) {
|
|
continue
|
|
}
|
|
|
|
joinableErrs = append(joinableErrs, err)
|
|
}
|
|
|
|
if allNil {
|
|
return nil
|
|
}
|
|
|
|
if len(joinableErrs) == 0 {
|
|
return BailError(errors.Join(errs...))
|
|
}
|
|
|
|
return errors.Join(joinableErrs...)
|
|
}
|