mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 10:41:03 +01:00
346 lines
6.6 KiB
Go
346 lines
6.6 KiB
Go
package errgroup
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// Check reports whether the "err" is not nil.
|
|
// If it is a group then it returns true if that or its children contains any error.
|
|
func Check(err error) error {
|
|
if isNotNil(err) {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Walk loops through each of the errors of "err".
|
|
// If "err" is *Group then it fires the "visitor" for each of its errors, including children.
|
|
// if "err" is *Error then it fires the "visitor" with its type and wrapped error.
|
|
// Otherwise it fires the "visitor" once with typ of nil and err as "err".
|
|
func Walk(err error, visitor func(typ interface{}, err error)) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
if group, ok := err.(*Group); ok {
|
|
list := group.getAllErrors()
|
|
for _, entry := range list {
|
|
if e, ok := entry.(*Error); ok {
|
|
visitor(e.Type, e.Err) // e.Unwrap() <-no.
|
|
} else {
|
|
visitor(nil, err)
|
|
}
|
|
}
|
|
} else if e, ok := err.(*Error); ok {
|
|
visitor(e.Type, e.Err)
|
|
} else {
|
|
visitor(nil, err)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
/*
|
|
func Errors(err error, conv bool) []error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
if group, ok := err.(*Group); ok {
|
|
list := group.getAllErrors()
|
|
if conv {
|
|
for i, entry := range list {
|
|
if _, ok := entry.(*Error); !ok {
|
|
list[i] = &Error{Err: entry, Type: group.Type}
|
|
}
|
|
}
|
|
}
|
|
|
|
return list
|
|
}
|
|
|
|
return []error{err}
|
|
}
|
|
|
|
func Type(err error) interface{} {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
if e, ok := err.(*Error); ok && e.Err != nil {
|
|
return e.Type
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func Fill(parent *Group, errors []*Error) {
|
|
for _, err := range errors {
|
|
if err.Type == parent.Type {
|
|
parent.Add(err)
|
|
continue
|
|
}
|
|
|
|
parent.Group(err.Type).Err(err)
|
|
}
|
|
return
|
|
}
|
|
*/
|
|
|
|
// Error implements the error interface.
|
|
// It is a special error type which keep the "Type" of the
|
|
// Group that it's created through Group's `Err` and `Errf` methods.
|
|
type Error struct {
|
|
Err error `json:"error" xml:"Error" yaml:"Error" toml:"Error" sql:"error"`
|
|
Type interface{} `json:"type" xml:"Type" yaml:"Type" toml:"Type" sql:"type"`
|
|
}
|
|
|
|
// Error returns the error message of the "Err".
|
|
func (e *Error) Error() string {
|
|
return e.Err.Error()
|
|
}
|
|
|
|
// Unwrap calls and returns the result of the "Err" Unwrap method or nil.
|
|
func (e *Error) Unwrap() error {
|
|
return errors.Unwrap(e.Err)
|
|
}
|
|
|
|
// Is reports whether the "err" is an *Error.
|
|
func (e *Error) Is(err error) bool {
|
|
if err == nil {
|
|
return false
|
|
}
|
|
|
|
ok := errors.Is(e.Err, err)
|
|
if !ok {
|
|
te, ok := err.(*Error)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
return errors.Is(e.Err, te.Err)
|
|
}
|
|
|
|
return ok
|
|
}
|
|
|
|
// As reports whether the "target" can be used as &Error{target.Type: ?}.
|
|
func (e *Error) As(target interface{}) bool {
|
|
if target == nil {
|
|
return target == e
|
|
}
|
|
|
|
ok := errors.As(e.Err, target)
|
|
if !ok {
|
|
te, ok := target.(*Error)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
if te.Type != nil {
|
|
if te.Type != e.Type {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return errors.As(e.Err, &te.Err)
|
|
}
|
|
|
|
return ok
|
|
}
|
|
|
|
// Group is an error container of a specific Type and can have child containers per type too.
|
|
type Group struct {
|
|
parent *Group
|
|
// a list of children groups, used to get or create new group through Group method.
|
|
children map[interface{}]*Group
|
|
depth int
|
|
|
|
Type interface{}
|
|
Errors []error // []*Error
|
|
|
|
// if true then this Group's Error method will return the messages of the errors made by this Group's Group method.
|
|
// Defaults to true.
|
|
IncludeChildren bool // it clones.
|
|
// IncludeTypeText bool
|
|
index int // group index.
|
|
}
|
|
|
|
// New returns a new empty Group.
|
|
func New(typ interface{}) *Group {
|
|
return &Group{
|
|
Type: typ,
|
|
IncludeChildren: true,
|
|
}
|
|
}
|
|
|
|
const delim = "\n"
|
|
|
|
func (g *Group) Error() (s string) {
|
|
if len(g.Errors) > 0 {
|
|
msgs := make([]string, len(g.Errors))
|
|
for i, err := range g.Errors {
|
|
msgs[i] = err.Error()
|
|
}
|
|
|
|
s = strings.Join(msgs, delim)
|
|
}
|
|
|
|
if g.IncludeChildren && len(g.children) > 0 {
|
|
// return with order of definition.
|
|
groups := g.getAllChildren()
|
|
sortGroups(groups)
|
|
|
|
for _, ge := range groups {
|
|
for _, childErr := range ge.Errors {
|
|
s += childErr.Error() + delim
|
|
}
|
|
}
|
|
|
|
if s != "" {
|
|
return s[:len(s)-1]
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (g *Group) getAllErrors() []error {
|
|
list := g.Errors
|
|
|
|
if len(g.children) > 0 {
|
|
// return with order of definition.
|
|
groups := g.getAllChildren()
|
|
sortGroups(groups)
|
|
|
|
for _, ge := range groups {
|
|
list = append(list, ge.Errors...)
|
|
}
|
|
}
|
|
|
|
return list
|
|
}
|
|
|
|
func (g *Group) getAllChildren() []*Group {
|
|
if len(g.children) == 0 {
|
|
return nil
|
|
}
|
|
|
|
var groups []*Group
|
|
for _, child := range g.children {
|
|
groups = append(groups, append([]*Group{child}, child.getAllChildren()...)...)
|
|
}
|
|
|
|
return groups
|
|
}
|
|
|
|
// Unwrap implements the dynamic std errors interface and it returns the parent Group.
|
|
func (g *Group) Unwrap() error {
|
|
if g == nil {
|
|
return nil
|
|
}
|
|
|
|
return g.parent
|
|
}
|
|
|
|
// Group creates a new group of "typ" type, if does not exist, and returns it.
|
|
func (g *Group) Group(typ interface{}) *Group {
|
|
if g.children == nil {
|
|
g.children = make(map[interface{}]*Group)
|
|
} else {
|
|
for _, child := range g.children {
|
|
if child.Type == typ {
|
|
return child
|
|
}
|
|
}
|
|
}
|
|
|
|
child := &Group{
|
|
Type: typ,
|
|
parent: g,
|
|
depth: g.depth + 1,
|
|
IncludeChildren: g.IncludeChildren,
|
|
index: g.index + 1 + len(g.children),
|
|
}
|
|
|
|
g.children[typ] = child
|
|
|
|
return child
|
|
}
|
|
|
|
// Add adds an error to the group.
|
|
func (g *Group) Add(err error) {
|
|
if err == nil {
|
|
return
|
|
}
|
|
|
|
g.Errors = append(g.Errors, err)
|
|
}
|
|
|
|
// Addf adds an error to the group like `fmt.Errorf` and returns it.
|
|
func (g *Group) Addf(format string, args ...interface{}) error {
|
|
err := fmt.Errorf(format, args...)
|
|
g.Add(err)
|
|
return err
|
|
}
|
|
|
|
// Err adds an error to the group, it transforms it to an Error type if necessary and returns it.
|
|
func (g *Group) Err(err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
e, ok := err.(*Error)
|
|
if !ok {
|
|
if ge, ok := err.(*Group); ok {
|
|
if g.children == nil {
|
|
g.children = make(map[interface{}]*Group)
|
|
}
|
|
|
|
g.children[ge.Type] = ge
|
|
return ge
|
|
}
|
|
|
|
e = &Error{err, 0}
|
|
}
|
|
e.Type = g.Type
|
|
|
|
g.Add(e)
|
|
return e
|
|
}
|
|
|
|
// Errf adds an error like `fmt.Errorf` and returns it.
|
|
func (g *Group) Errf(format string, args ...interface{}) error {
|
|
return g.Err(fmt.Errorf(format, args...))
|
|
}
|
|
|
|
func sortGroups(groups []*Group) {
|
|
sort.Slice(groups, func(i, j int) bool {
|
|
return groups[i].index < groups[j].index
|
|
})
|
|
}
|
|
|
|
func isNotNil(err error) bool {
|
|
if g, ok := err.(*Group); ok {
|
|
if len(g.Errors) > 0 {
|
|
return true
|
|
}
|
|
|
|
if len(g.children) > 0 {
|
|
for _, child := range g.children {
|
|
if isNotNil(child) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
return err != nil
|
|
}
|