rclone/vendor/github.com/spacemonkeygo/errors/utils.go

156 lines
4.1 KiB
Go
Raw Normal View History

2020-05-11 20:57:46 +02:00
// Copyright (C) 2014 Space Monkey, Inc.
//
// 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 errors
import (
"fmt"
"log"
"runtime"
"strings"
)
var (
// Change this method if you want errors to log somehow else
LogMethod = log.Printf
ErrorGroupError = NewClass("Error Group Error")
)
// LogWithStack will log the given messages with the current stack
func LogWithStack(messages ...interface{}) {
buf := make([]byte, Config.Stacklogsize)
buf = buf[:runtime.Stack(buf, false)]
LogMethod("%s\n%s", fmt.Sprintln(messages...), buf)
}
// CatchPanic can be used to catch panics and turn them into errors. See the
// example.
func CatchPanic(err_ref *error) {
r := recover()
if r == nil {
return
}
err, ok := r.(error)
if ok {
*err_ref = PanicError.Wrap(err)
return
}
*err_ref = PanicError.New("%v", r)
}
// ErrorGroup is a type for collecting errors from a bunch of independent
// tasks. ErrorGroups are not threadsafe. See the example for usage.
type ErrorGroup struct {
Errors []error
limit int
excess int
}
// NewErrorGroup makes a new ErrorGroup
func NewErrorGroup() *ErrorGroup { return &ErrorGroup{} }
// NewBoundedErrorGroup makes a new ErrorGroup that will not track more than
// limit errors. Once the limit is reached, the ErrorGroup will track
// additional errors as excess.
func NewBoundedErrorGroup(limit int) *ErrorGroup {
return &ErrorGroup{
limit: limit,
}
}
// Add is called with errors. nil errors are ignored.
func (e *ErrorGroup) Add(err error) {
if err == nil {
return
}
if e.limit > 0 && len(e.Errors) == e.limit {
e.excess++
} else {
e.Errors = append(e.Errors, err)
}
}
// Finalize will collate all the found errors. If no errors were found, it will
// return nil. If one error was found, it will be returned directly. Otherwise
// an ErrorGroupError will be returned.
func (e *ErrorGroup) Finalize() error {
if len(e.Errors) == 0 {
return nil
}
if len(e.Errors) == 1 && e.excess == 0 {
return e.Errors[0]
}
msgs := make([]string, 0, len(e.Errors))
for _, err := range e.Errors {
msgs = append(msgs, err.Error())
}
if e.excess > 0 {
msgs = append(msgs, fmt.Sprintf("... and %d more.", e.excess))
e.excess = 0
}
e.Errors = nil
return ErrorGroupError.New(strings.Join(msgs, "\n"))
}
// LoggingErrorGroup is similar to ErrorGroup except that instead of collecting
// all of the errors, it logs the errors immediately and just counts how many
// non-nil errors have been seen. See the ErrorGroup example for usage.
type LoggingErrorGroup struct {
name string
total int
failed int
}
// NewLoggingErrorGroup returns a new LoggingErrorGroup with the given name.
func NewLoggingErrorGroup(name string) *LoggingErrorGroup {
return &LoggingErrorGroup{name: name}
}
// Add will handle a given error. If the error is non-nil, total and failed
// are both incremented and the error is logged. If the error is nil, only
// total is incremented.
func (e *LoggingErrorGroup) Add(err error) {
e.total++
if err != nil {
LogMethod("%s: %s", e.name, err)
e.failed++
}
}
// Finalize returns no error if no failures were observed, otherwise it will
// return an ErrorGroupError with statistics about the observed errors.
func (e *LoggingErrorGroup) Finalize() (err error) {
if e.failed > 0 {
err = ErrorGroupError.New("%s: %d of %d failed.", e.name, e.failed,
e.total)
}
e.total = 0
e.failed = 0
return err
}
type Finalizer interface {
Finalize() error
}
// Finalize takes a group of ErrorGroups and joins them together into one error
func Finalize(finalizers ...Finalizer) error {
var errs ErrorGroup
for _, finalizer := range finalizers {
errs.Add(finalizer.Finalize())
}
return errs.Finalize()
}