Files
opencloud/vendor/github.com/olekukonko/errors/group.go
T
dependabot[bot] 9dbde7952d build(deps): bump github.com/olekukonko/errors from 1.2.0 to 1.3.0
Bumps [github.com/olekukonko/errors](https://github.com/olekukonko/errors) from 1.2.0 to 1.3.0.
- [Commits](https://github.com/olekukonko/errors/compare/v1.2.0...v1.3.0)

---
updated-dependencies:
- dependency-name: github.com/olekukonko/errors
  dependency-version: 1.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-21 10:28:36 +02:00

120 lines
3.4 KiB
Go

// Group runs multiple functions concurrently and collects all errors into a
// *MultiError. It is the error-aware counterpart to sync/errgroup: errgroup
// stops at the first failure; Group collects every failure.
package errors
import (
"context"
"sync"
)
// Group runs goroutines concurrently and collects every error they return.
// The zero value is ready to use; options may be applied via NewGroup.
//
// Example — fan-out with full error collection:
//
// g := errors.NewGroup()
// g.Go(func() error { return validateUser(id) })
// g.Go(func() error { return validatePerms(id) })
// if err := g.Wait(); err != nil {
// // err is *MultiError containing all failures
// log.Println(err)
// }
type Group struct {
wg sync.WaitGroup
errs *MultiError
ctx context.Context
cancel context.CancelFunc
cancelOnFirst bool
}
// GroupOption configures a Group.
type GroupOption func(*Group)
// GroupWithContext attaches ctx to the Group. The ctx is passed to
// context-aware Go calls (GoCtx). If cancelOnFirst is true, the context
// is cancelled as soon as the first error is returned by any goroutine —
// useful for "cancel siblings on first failure" patterns.
func GroupWithContext(ctx context.Context, cancelOnFirst bool) GroupOption {
return func(g *Group) {
g.ctx, g.cancel = context.WithCancel(ctx)
g.cancelOnFirst = cancelOnFirst
}
}
// GroupWithLimit sets a maximum error limit on the underlying MultiError.
// Errors beyond the limit are dropped.
func GroupWithLimit(n int) GroupOption {
return func(g *Group) {
g.errs = NewMultiError(WithLimit(n))
}
}
// NewGroup creates a Group with the given options applied.
func NewGroup(opts ...GroupOption) *Group {
g := &Group{
errs: NewMultiError(),
}
for _, o := range opts {
o(g)
}
if g.ctx == nil {
g.ctx = context.Background()
}
return g
}
// Go starts fn in a new goroutine. Errors returned by fn are collected;
// nil returns are ignored. Thread-safe: MultiError.Add handles its own locking.
// cancelOnFirst is read-only after construction so no lock is needed.
func (g *Group) Go(fn func() error) {
g.wg.Add(1)
go func() {
defer g.wg.Done()
if err := fn(); err != nil {
g.errs.Add(err) // MultiError.Add is internally mutex-protected
if g.cancelOnFirst && g.cancel != nil {
g.cancel()
}
}
}()
}
// GoCtx starts fn in a new goroutine, passing the group's context.
// If the group was created with GroupWithContext, fn receives a context
// that is cancelled when cancelOnFirst triggers or the parent is done.
func (g *Group) GoCtx(fn func(ctx context.Context) error) {
g.wg.Add(1)
go func() {
defer g.wg.Done()
if err := fn(g.ctx); err != nil {
g.errs.Add(err) // MultiError.Add is internally mutex-protected
if g.cancelOnFirst && g.cancel != nil {
g.cancel()
}
}
}()
}
// Wait blocks until all goroutines have finished and returns a *MultiError
// containing every error collected, or nil if all succeeded.
// Always returns *MultiError (never collapses to a raw error) so callers
// can reliably type-assert the result.
func (g *Group) Wait() error {
g.wg.Wait()
if g.cancel != nil {
g.cancel() // release context resources
}
if !g.errs.Has() {
return nil
}
return g.errs
}
// Errors returns a snapshot of errors collected so far.
// Safe to call concurrently with Go/GoCtx; may be incomplete before Wait returns.
func (g *Group) Errors() []error {
return g.errs.Errors()
}