mirror of
https://github.com/rclone/rclone.git
synced 2024-12-12 02:02:02 +01:00
156 lines
4.1 KiB
Go
156 lines
4.1 KiB
Go
|
// 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()
|
||
|
}
|