From ae10e7491f210e3cf0478cfc124ae91c1596bab0 Mon Sep 17 00:00:00 2001 From: harshabose Date: Sat, 12 Jul 2025 18:18:45 +0530 Subject: [PATCH] Re-add tools submodule properly --- dependencies/tools | 1 + dependencies/tools/.gitignore | 50 --- dependencies/tools/go.mod | 7 - dependencies/tools/go.sum | 12 - dependencies/tools/pkg/buffer/errors.go | 8 - dependencies/tools/pkg/buffer/frame_pool.go | 55 --- dependencies/tools/pkg/buffer/limit_buffer.go | 144 -------- dependencies/tools/pkg/buffer/package.go | 24 -- dependencies/tools/pkg/buffer/packet_pool.go | 55 --- dependencies/tools/pkg/metrics/metrics.go | 338 ------------------ dependencies/tools/pkg/multierr/multierr.go | 116 ------ 11 files changed, 1 insertion(+), 809 deletions(-) create mode 160000 dependencies/tools delete mode 100644 dependencies/tools/.gitignore delete mode 100644 dependencies/tools/go.mod delete mode 100644 dependencies/tools/go.sum delete mode 100644 dependencies/tools/pkg/buffer/errors.go delete mode 100644 dependencies/tools/pkg/buffer/frame_pool.go delete mode 100644 dependencies/tools/pkg/buffer/limit_buffer.go delete mode 100644 dependencies/tools/pkg/buffer/package.go delete mode 100644 dependencies/tools/pkg/buffer/packet_pool.go delete mode 100644 dependencies/tools/pkg/metrics/metrics.go delete mode 100644 dependencies/tools/pkg/multierr/multierr.go diff --git a/dependencies/tools b/dependencies/tools new file mode 160000 index 0000000..d5ed322 --- /dev/null +++ b/dependencies/tools @@ -0,0 +1 @@ +Subproject commit d5ed3222607ba76f0fd55bc7ccae29c7ed3089a4 diff --git a/dependencies/tools/.gitignore b/dependencies/tools/.gitignore deleted file mode 100644 index 7a288b3..0000000 --- a/dependencies/tools/.gitignore +++ /dev/null @@ -1,50 +0,0 @@ -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, built with `go test -c` -*.test - -# Output of the go coverage tool -*.out - -# Go workspace file -go.work - -# Dependency directories -vendor/ - -# Build directories -build/ -third_party/ - -# Environment files -*.env -!.env.example - -# macOS -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes - -# Windows -ehthumbs.db -Thumbs.db - -# Linux -*~ - -# IDE files -.vscode/ -.idea/ -*.swp -*.swo - -# Logs -*.log -logs/ \ No newline at end of file diff --git a/dependencies/tools/go.mod b/dependencies/tools/go.mod deleted file mode 100644 index 2d8e23a..0000000 --- a/dependencies/tools/go.mod +++ /dev/null @@ -1,7 +0,0 @@ -module github.com/harshabose/tools - -go 1.24.1 - -require github.com/asticode/go-astiav v0.37.0 - -require github.com/asticode/go-astikit v0.42.0 // indirect diff --git a/dependencies/tools/go.sum b/dependencies/tools/go.sum deleted file mode 100644 index cc69ada..0000000 --- a/dependencies/tools/go.sum +++ /dev/null @@ -1,12 +0,0 @@ -github.com/asticode/go-astiav v0.37.0 h1:Ph4usW4lulotVvne8hqZ1JCOHX1f8ces6yVKdg+PnyQ= -github.com/asticode/go-astiav v0.37.0/go.mod h1:GI0pHw6K2/pl/o8upCtT49P/q4KCwhv/8nGLlCsZLdA= -github.com/asticode/go-astikit v0.42.0 h1:pnir/2KLUSr0527Tv908iAH6EGYYrYta132vvjXsH5w= -github.com/asticode/go-astikit v0.42.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/dependencies/tools/pkg/buffer/errors.go b/dependencies/tools/pkg/buffer/errors.go deleted file mode 100644 index 74507d5..0000000 --- a/dependencies/tools/pkg/buffer/errors.go +++ /dev/null @@ -1,8 +0,0 @@ -package buffer - -import "errors" - -var ( - ErrorElementUnallocated = errors.New("encountered nil in the buffer. this should not happen. check usage") - ErrorChannelBufferClose = errors.New("channel buffer has be closed. cannot perform this operation") -) diff --git a/dependencies/tools/pkg/buffer/frame_pool.go b/dependencies/tools/pkg/buffer/frame_pool.go deleted file mode 100644 index c1f6b4c..0000000 --- a/dependencies/tools/pkg/buffer/frame_pool.go +++ /dev/null @@ -1,55 +0,0 @@ -//go:build cgo_enabled - -package buffer - -import ( - "sync" - - "github.com/asticode/go-astiav" -) - -type framePool struct { - pool sync.Pool -} - -func CreateFramePool() Pool[*astiav.Frame] { - return &framePool{ - pool: sync.Pool{ - New: func() any { - return astiav.AllocFrame() - }, - }, - } -} - -func (pool *framePool) Get() *astiav.Frame { - frame, ok := pool.pool.Get().(*astiav.Frame) - - if frame == nil || !ok { - return astiav.AllocFrame() - } - return frame -} - -func (pool *framePool) Put(frame *astiav.Frame) { - if frame == nil { - return - } - - frame.Unref() - pool.pool.Put(frame) -} - -func (pool *framePool) Release() { - for { - frame, ok := pool.pool.Get().(*astiav.Frame) - if frame == nil { - break - } - if !ok { - continue - } - - frame.Free() - } -} diff --git a/dependencies/tools/pkg/buffer/limit_buffer.go b/dependencies/tools/pkg/buffer/limit_buffer.go deleted file mode 100644 index 24f59c8..0000000 --- a/dependencies/tools/pkg/buffer/limit_buffer.go +++ /dev/null @@ -1,144 +0,0 @@ -package buffer - -import ( - "context" - "errors" - "fmt" - "sync" -) - -type ChannelBuffer[T any] struct { - pool Pool[T] - bufferChannel chan T - inputBuffer chan T - closed bool - mux sync.RWMutex - ctx context.Context -} - -func CreateChannelBuffer[T any](ctx context.Context, size int, pool Pool[T]) *ChannelBuffer[T] { - buffer := &ChannelBuffer[T]{ - pool: pool, - bufferChannel: make(chan T, size), - inputBuffer: make(chan T, size), - closed: false, - ctx: ctx, - } - go buffer.loop() - return buffer -} - -func (buffer *ChannelBuffer[T]) Push(ctx context.Context, element T) error { - buffer.mux.RLock() - defer buffer.mux.RUnlock() - - if buffer.closed { - return errors.New("buffer closed") - } - select { - case buffer.inputBuffer <- element: - // WARN: LACKS CHECKS FOR CLOSED CHANNEL - return nil - case <-ctx.Done(): - return ctx.Err() - } -} - -func (buffer *ChannelBuffer[T]) Pop(ctx context.Context) (T, error) { - buffer.mux.RLock() - defer buffer.mux.RUnlock() - - var zero T - - if buffer.closed { - return zero, errors.New("buffer closed") - } - select { - case <-ctx.Done(): - return zero, ctx.Err() - case data, ok := <-buffer.bufferChannel: - if !ok { - return zero, ErrorChannelBufferClose - } - // TODO: NIL CHECK IS REQUIRED BUT GENERIC CANNOT DO THIS. SOLVE THIS ASAP - // if data == nil { - // return zero, ErrorElementUnallocated - // } - return data, nil - } -} - -func (buffer *ChannelBuffer[T]) Generate() T { - return buffer.pool.Get() -} - -func (buffer *ChannelBuffer[T]) PutBack(element T) { - if buffer.pool != nil { - buffer.pool.Put(element) - } -} - -func (buffer *ChannelBuffer[T]) GetChannel() chan T { - return buffer.bufferChannel -} - -func (buffer *ChannelBuffer[T]) Size() int { - return len(buffer.bufferChannel) -} - -func (buffer *ChannelBuffer[T]) loop() { - defer buffer.close() -loop: - for { - select { - case <-buffer.ctx.Done(): - return - case element, ok := <-buffer.inputBuffer: - // if !ok || element == nil { - // continue loop - // } - if !ok { - continue loop - } - select { - case buffer.bufferChannel <- element: // SUCCESSFULLY BUFFERED - continue loop - default: - select { - case oldElement := <-buffer.bufferChannel: - buffer.PutBack(oldElement) - select { - case buffer.bufferChannel <- element: - continue loop - default: - fmt.Println("unexpected buffer state. skipping the element..") - buffer.PutBack(element) - } - } - } - } - } -} - -func (buffer *ChannelBuffer[T]) close() { - buffer.mux.Lock() - buffer.closed = true - buffer.mux.Unlock() - -loop: - for { - select { - case element := <-buffer.bufferChannel: - if buffer.pool != nil { - buffer.pool.Put(element) - } - default: - close(buffer.bufferChannel) - close(buffer.inputBuffer) - break loop - } - } - if buffer.pool != nil { - buffer.pool.Release() - } -} diff --git a/dependencies/tools/pkg/buffer/package.go b/dependencies/tools/pkg/buffer/package.go deleted file mode 100644 index 019a03f..0000000 --- a/dependencies/tools/pkg/buffer/package.go +++ /dev/null @@ -1,24 +0,0 @@ -// TODO: CLEAN THIS; THIS IS STUPID - -package buffer - -import "context" - -type Pool[T any] interface { - Get() T - Put(T) - Release() -} - -type Buffer[T any] interface { - Push(context.Context, T) error - Pop(ctx context.Context) (T, error) - Size() int -} - -type BufferWithGenerator[T any] interface { - Buffer[T] - Generate() T - PutBack(T) - GetChannel() chan T -} diff --git a/dependencies/tools/pkg/buffer/packet_pool.go b/dependencies/tools/pkg/buffer/packet_pool.go deleted file mode 100644 index a2861b7..0000000 --- a/dependencies/tools/pkg/buffer/packet_pool.go +++ /dev/null @@ -1,55 +0,0 @@ -//go:build cgo_enabled - -package buffer - -import ( - "sync" - - "github.com/asticode/go-astiav" -) - -type packetPool struct { - pool sync.Pool -} - -func CreatePacketPool() Pool[*astiav.Packet] { - return &packetPool{ - pool: sync.Pool{ - New: func() any { - return astiav.AllocPacket() - }, - }, - } -} - -func (pool *packetPool) Get() *astiav.Packet { - packet, ok := pool.pool.Get().(*astiav.Packet) - - if packet == nil || !ok { - return astiav.AllocPacket() - } - return packet -} - -func (pool *packetPool) Put(packet *astiav.Packet) { - if packet == nil { - return - } - - packet.Unref() - pool.pool.Put(packet) -} - -func (pool *packetPool) Release() { - for { - packet, ok := pool.pool.Get().(*astiav.Packet) - if packet == nil { - break - } - if !ok { - continue - } - // fmt.Printf("🗑️ Releasing packet: ptr=%p\n", packet) - packet.Free() - } -} diff --git a/dependencies/tools/pkg/metrics/metrics.go b/dependencies/tools/pkg/metrics/metrics.go deleted file mode 100644 index dde8ce3..0000000 --- a/dependencies/tools/pkg/metrics/metrics.go +++ /dev/null @@ -1,338 +0,0 @@ -package metrics - -import ( - "context" - "encoding/json" - "fmt" - "sync" - "time" -) - -type ClientState int - -const ( - ClientStateDisconnected ClientState = iota - ClientStateConnecting - ClientStateConnected - ClientStateError -) - -func (cs ClientState) String() string { - switch cs { - case ClientStateDisconnected: - return "Disconnected" - case ClientStateConnecting: - return "Connecting" - case ClientStateConnected: - return "Connected" - case ClientStateError: - return "Error" - default: - return "Unknown" - } -} - -type BufferedErrors struct { - maxSize int - errors []ErrorEntry - mux sync.RWMutex -} - -type ErrorEntry struct { - Timestamp time.Time `json:"timestamp"` - Message string `json:"message"` -} - -func NewBufferedErrors(maxSize int) *BufferedErrors { - return &BufferedErrors{ - maxSize: maxSize, - errors: make([]ErrorEntry, 0, maxSize), - } -} - -func (be *BufferedErrors) Add(err error) { - be.mux.Lock() - defer be.mux.Unlock() - - if len(be.errors) >= be.maxSize { - be.errors = be.errors[1:] - } - - be.errors = append(be.errors, ErrorEntry{ - Timestamp: time.Now(), - Message: err.Error(), - }) -} - -func (be *BufferedErrors) MarshalJSON() ([]byte, error) { - be.mux.RLock() - defer be.mux.RUnlock() - - return json.Marshal(be.errors) -} - -type MetricsSnapshot struct { - State string `json:"state"` - PacketsRead uint64 `json:"packetsRead"` - PacketsWritten uint64 `json:"packetsWritten"` - BytesRead uint64 `json:"bytesRead"` - BytesWritten uint64 `json:"bytesWritten"` - BytesReadRate float32 `json:"bytesReadRate"` - BytesWrittenRate float32 `json:"bytesWrittenRate"` - LastWriteTime *time.Time `json:"lastWriteTime,omitempty"` - LastReadTime *time.Time `json:"lastReadTime,omitempty"` - RecentErrors *BufferedErrors `json:"recentErrors"` -} - -type UnifiedMetrics struct { - // Basic state - state ClientState - packetsRead uint64 - packetsWritten uint64 - bytesRead uint64 - bytesWritten uint64 - lastWriteTime time.Time - lastReadTime time.Time - recentErrors *BufferedErrors - - // Rate calculation - lastBytesRead uint64 - lastBytesWritten uint64 - bytesReadRate float32 - bytesWrittenRate float32 - tickerInterval time.Duration - - // Control - serviceTitle string - ctx context.Context - cancel context.CancelFunc - wg sync.WaitGroup - mux sync.RWMutex -} - -func NewUnifiedMetrics(ctx context.Context, serviceTitle string, maxErrorBuffer int, tickerInterval time.Duration) *UnifiedMetrics { - ctx2, cancel := context.WithCancel(ctx) - m := &UnifiedMetrics{ - state: ClientStateDisconnected, - recentErrors: NewBufferedErrors(maxErrorBuffer), - serviceTitle: serviceTitle, - tickerInterval: tickerInterval, - ctx: ctx2, - cancel: cancel, - } - - m.wg.Add(1) - go m.loop() - - return m -} - -func (m *UnifiedMetrics) loop() { - defer m.wg.Done() - - ticker := time.NewTicker(m.tickerInterval) - defer ticker.Stop() - - for { - select { - case <-m.ctx.Done(): - return - case <-ticker.C: - m.updateRatesAndPrint() - } - } -} - -func (m *UnifiedMetrics) updateRatesAndPrint() { - m.mux.Lock() - - currentBytesRead := m.bytesRead - currentBytesWritten := m.bytesWritten - - intervalSeconds := float32(m.tickerInterval.Seconds()) - m.bytesReadRate = float32(currentBytesRead-m.lastBytesRead) / intervalSeconds - m.bytesWrittenRate = float32(currentBytesWritten-m.lastBytesWritten) / intervalSeconds - - m.lastBytesRead = currentBytesRead - m.lastBytesWritten = currentBytesWritten - - m.mux.Unlock() - - snapshot := m.GetSnapshot() - - m.printFormattedMetrics(snapshot) -} - -func (m *UnifiedMetrics) printFormattedMetrics(snapshot MetricsSnapshot) { - divider := "================================================" - - fmt.Printf("\n%s\n", divider) - fmt.Printf("%*s\n", (len(divider)+len(m.serviceTitle))/2, m.serviceTitle) - fmt.Printf("%s\n\n", divider) - - fmt.Printf("%-20s: %s\n", "Connection State", snapshot.State) - fmt.Println() - - fmt.Printf("%-20s: %d\n", "Packets Read", snapshot.PacketsRead) - fmt.Printf("%-20s: %d\n", "Packets Written", snapshot.PacketsWritten) - fmt.Println() - - fmt.Printf("%-20s: %s\n", "Bytes Read", formatBytes(snapshot.BytesRead)) - fmt.Printf("%-20s: %s\n", "Bytes Written", formatBytes(snapshot.BytesWritten)) - fmt.Println() - - fmt.Printf("%-20s: %s/s\n", "Read Rate", formatBytes(uint64(snapshot.BytesReadRate))) - fmt.Printf("%-20s: %s/s\n", "Write Rate", formatBytes(uint64(snapshot.BytesWrittenRate))) - fmt.Println() - - if snapshot.LastReadTime != nil { - fmt.Printf("%-20s: %s\n", "Last Read", snapshot.LastReadTime.Format("2006-01-02 15:04:05")) - } - if snapshot.LastWriteTime != nil { - fmt.Printf("%-20s: %s\n", "Last Write", snapshot.LastWriteTime.Format("2006-01-02 15:04:05")) - } - if snapshot.LastReadTime != nil || snapshot.LastWriteTime != nil { - fmt.Println() - } - - snapshot.RecentErrors.mux.RLock() - errorCount := len(snapshot.RecentErrors.errors) - if errorCount > 0 { - fmt.Printf("Recent Errors (%d error", errorCount) - if errorCount != 1 { - fmt.Print("s") - } - fmt.Println("):") - - for _, errorEntry := range snapshot.RecentErrors.errors { - fmt.Printf(" [%s] %s\n", - errorEntry.Timestamp.Format("15:04:05"), - errorEntry.Message) - } - fmt.Println() - } - snapshot.RecentErrors.mux.RUnlock() - - fmt.Printf("%s\n", divider) -} - -func formatBytes(bytes uint64) string { - if bytes < 1024 { - return fmt.Sprintf("%d B", bytes) - } else if bytes < 1024*1024 { - return fmt.Sprintf("%.2f KB", float64(bytes)/1024) - } else if bytes < 1024*1024*1024 { - return fmt.Sprintf("%.2f MB", float64(bytes)/(1024*1024)) - } else { - return fmt.Sprintf("%.2f GB", float64(bytes)/(1024*1024*1024)) - } -} - -func (m *UnifiedMetrics) Close() error { - if m.cancel != nil { - m.cancel() - m.wg.Wait() - } - return nil -} - -func (m *UnifiedMetrics) SetState(state ClientState) { - m.mux.Lock() - defer m.mux.Unlock() - m.state = state -} - -func (m *UnifiedMetrics) GetState() ClientState { - m.mux.RLock() - defer m.mux.RUnlock() - return m.state -} - -func (m *UnifiedMetrics) IncrementPacketsWritten() { - m.mux.Lock() - defer m.mux.Unlock() - m.packetsWritten++ -} - -func (m *UnifiedMetrics) IncrementPacketsRead() { - m.mux.Lock() - defer m.mux.Unlock() - m.packetsRead++ -} - -func (m *UnifiedMetrics) IncrementBytesWritten(bytes uint64) { - m.mux.Lock() - defer m.mux.Unlock() - m.bytesWritten += bytes -} - -func (m *UnifiedMetrics) IncrementBytesRead(bytes uint64) { - m.mux.Lock() - defer m.mux.Unlock() - m.bytesRead += bytes -} - -func (m *UnifiedMetrics) SetLastWriteTime(t time.Time) { - m.mux.Lock() - defer m.mux.Unlock() - m.lastWriteTime = t -} - -func (m *UnifiedMetrics) SetLastReadTime(t time.Time) { - m.mux.Lock() - defer m.mux.Unlock() - m.lastReadTime = t -} - -func (m *UnifiedMetrics) GetLastWriteTime() time.Time { - m.mux.RLock() - defer m.mux.RUnlock() - return m.lastWriteTime -} - -func (m *UnifiedMetrics) GetLastReadTime() time.Time { - m.mux.RLock() - defer m.mux.RUnlock() - return m.lastReadTime -} - -func (m *UnifiedMetrics) AddErrors(errs ...error) { - if len(errs) == 0 { - return - } - - m.mux.Lock() - defer m.mux.Unlock() - - for _, err := range errs { - if err == nil { - continue - } - m.recentErrors.Add(err) - } -} - -func (m *UnifiedMetrics) GetSnapshot() MetricsSnapshot { - m.mux.RLock() - defer m.mux.RUnlock() - - snapshot := MetricsSnapshot{ - State: m.state.String(), - PacketsRead: m.packetsRead, - PacketsWritten: m.packetsWritten, - BytesRead: m.bytesRead, - BytesWritten: m.bytesWritten, - BytesReadRate: m.bytesReadRate, - BytesWrittenRate: m.bytesWrittenRate, - RecentErrors: m.recentErrors, - } - - if !m.lastReadTime.IsZero() { - snapshot.LastReadTime = &m.lastReadTime - } - if !m.lastWriteTime.IsZero() { - snapshot.LastWriteTime = &m.lastWriteTime - } - - return snapshot -} diff --git a/dependencies/tools/pkg/multierr/multierr.go b/dependencies/tools/pkg/multierr/multierr.go deleted file mode 100644 index a9d2290..0000000 --- a/dependencies/tools/pkg/multierr/multierr.go +++ /dev/null @@ -1,116 +0,0 @@ -// Package multierr provides utilities for combining multiple errors into a single error. -package multierr - -import ( - "errors" - "fmt" - "strings" -) - -// Combine takes a list of errors and combines them into a single error. -// If all errors are nil, it returns nil. -// If there's only one non-nil error, it returns that error. -// If there are multiple non-nil errors, it combines them into a single error. -func Combine(errs ...error) error { - var nonNilErrs []error - for _, err := range errs { - if err != nil { - nonNilErrs = append(nonNilErrs, err) - } - } - - if len(nonNilErrs) == 0 { - return nil - } - - if len(nonNilErrs) == 1 { - return nonNilErrs[0] - } - - return &multiError{ - errors: nonNilErrs, - } -} - -// Append combines the two errors into a single error. -// If both errors are nil, it returns nil. -// If one error is nil, it returns the non-nil error. -// If both errors are non-nil, it combines them into a single error. -func Append(err1, err2 error) error { - if err1 == nil { - return err2 - } - - if err2 == nil { - return err1 - } - - // If err1 is already a multiError, append err2 to it - if me, ok := err1.(*multiError); ok { - return &multiError{ - errors: append(me.errors, err2), - } - } - - // If err2 is already a multiError, prepend err1 to it - if me, ok := err2.(*multiError); ok { - return &multiError{ - errors: append([]error{err1}, me.errors...), - } - } - - // Neither error is a multiError, create a new one - return &multiError{ - errors: []error{err1, err2}, - } -} - -// multiError is an implementation of error that contains multiple errors. -type multiError struct { - errors []error -} - -// Error returns a string representation of all the errors. -func (m *multiError) Error() string { - if len(m.errors) == 0 { - return "" - } - - if len(m.errors) == 1 { - return m.errors[0].Error() - } - - var sb strings.Builder - sb.WriteString(fmt.Sprintf("%d errors occurred:\n", len(m.errors))) - for i, err := range m.errors { - sb.WriteString(fmt.Sprintf(" %d: %s\n", i+1, err.Error())) - } - return sb.String() -} - -// Unwrap returns the underlying errors. -// This allows errors.Is and errors.As to work with multiError. -func (m *multiError) Unwrap() []error { - return m.errors -} - -// Is reports whether any error in multiError matches target. -func (m *multiError) Is(target error) bool { - for _, err := range m.errors { - if errors.Is(err, target) { - return true - } - } - return false -} - -// As finds the first error in multiError that matches target, and if one is found, sets -// target to that error value and returns true. Otherwise, it returns false. -func (m *multiError) As(target interface{}) bool { - for _, err := range m.errors { - if errors.As(err, target) { - return true - } - } - return false -}