From ab159d942623ec711232b059eb3a24d7a42b78a2 Mon Sep 17 00:00:00 2001 From: Tigor Hutasuhut Date: Tue, 27 Aug 2024 17:08:52 +0700 Subject: [PATCH] zerr: sequence: changed into interface for future proofing --- core/zerr/err.go | 4 +-- core/zerr/err_write_to.go | 6 ++-- core/zerr/error.go | 4 ++- core/zerr/sequence.go | 66 ++++++++++++++++++++++++++++----------- core/zerr/wrap.go | 38 +++++++++++++++++----- go.mod | 4 +-- 6 files changed, 89 insertions(+), 33 deletions(-) diff --git a/core/zerr/err.go b/core/zerr/err.go index b3802f5..cfbf101 100644 --- a/core/zerr/err.go +++ b/core/zerr/err.go @@ -27,7 +27,7 @@ type Err struct { id string logger Logger notifier Notifier - sequence *Sequence + sequence Sequence } // Join implements Error. @@ -215,7 +215,7 @@ func (mu *Err) Notify(ctx context.Context, opts ...zoptions.NotifyOption) Error } // Sequence returns the state of the error sequence. -func (mu *Err) Sequence() *Sequence { +func (mu *Err) Sequence() Sequence { return mu.sequence } diff --git a/core/zerr/err_write_to.go b/core/zerr/err_write_to.go index f190050..6f18f46 100644 --- a/core/zerr/err_write_to.go +++ b/core/zerr/err_write_to.go @@ -18,8 +18,10 @@ func (er *Err) WriteBuilder(w io.Writer) { return } var previous string - if err := er.sequence.Outer(); err != nil && !err.IsMulti() { - previous = err.GetMessage() + if parent := er.sequence.Parent(); parent != nil { + if err := parent.Error(); err != nil && !err.IsMulti() { + previous = err.GetMessage() + } } current := strings.TrimPrefix(er.message, previous) if current != "" { diff --git a/core/zerr/error.go b/core/zerr/error.go index 56984ed..08802de 100644 --- a/core/zerr/error.go +++ b/core/zerr/error.go @@ -85,7 +85,9 @@ type Error interface { Notify(ctx context.Context, opts ...zoptions.NotifyOption) Error // Sequence returns the state of the error sequence. - Sequence() *Sequence + // + // Sequence must not be nil. + Sequence() Sequence // IsMulti returns true if the error is a multiple error. IsMulti() bool diff --git a/core/zerr/sequence.go b/core/zerr/sequence.go index 4b93e44..8088fb0 100644 --- a/core/zerr/sequence.go +++ b/core/zerr/sequence.go @@ -9,24 +9,50 @@ import "iter" // When wrapping errors, the wrapping error // will be the parent of the wrapped Error // if the wrapped Error is a zerr.Error. -type Sequence struct { - parent *Sequence - children []*Sequence +type Sequence interface { + // WrapBy prepends the next Sequence to the current Sequence. + WrapBy(parent Sequence) + + // Parent returns the parent Sequence. + // Return nil if this Sequence is Root. + Parent() Sequence + + // Root returns the outermost Sequence in the sequence. + // + // Returns self if current Sequence is the outermost. + Root() Sequence + + // IsRoot checks if the Sequence is the outermost Sequence in the sequence. + IsRoot() bool + + // Error returns the Error in the sequence. + Error() Error + + // Iter returns an iterator to traverse Errors in the sequence. + Iter() iter.Seq[Error] + + // AppendChild appends a child Sequence to the current Sequence. + AppendChild(child Sequence) +} + +type sequence struct { + parent Sequence + children []Sequence err Error } -// WrapBy prepends the next Sequence to the current Sequence. -func (se *Sequence) WrapBy(parent *Sequence) { - parent.children = append(parent.children, se) +func (se *sequence) WrapBy(parent Sequence) { + parent.AppendChild(se) se.parent = parent } -// Outer returns the error that wraps this Error. -// Return nil if this Error is not wrapped -// or wrapping outer is not a zerr.Error. -func (se *Sequence) Outer() Error { +func (se *sequence) AppendChild(child Sequence) { + se.children = append(se.children, child) +} + +func (se *sequence) Parent() Sequence { if se.parent != nil { - return se.parent.err + return se.parent } return nil } @@ -34,21 +60,23 @@ func (se *Sequence) Outer() Error { // Root returns the outermost Error in the sequence. // // Returns self if there is no outermost. -func (se *Sequence) Root() *Sequence { - root := se - for root.parent != nil { - root = root.parent +func (se *sequence) Root() Sequence { + var root Sequence = se + for { + if root.Parent() == nil { + return root + } + root = root.Parent() } - return root } // IsRoot checks if the Error is the outermost Error in the sequence. -func (se *Sequence) IsRoot() bool { +func (se *sequence) IsRoot() bool { return se.parent == nil } // Error returns the Error in the sequence. -func (se *Sequence) Error() Error { +func (se *sequence) Error() Error { return se.err } @@ -64,7 +92,7 @@ func (se *Sequence) Error() Error { // // e is zerr.Error // // do something with e // } -func (se *Sequence) Iter() iter.Seq[Error] { +func (se *sequence) Iter() iter.Seq[Error] { return func(yield func(Error) bool) { if se.err != nil && !yield(se.err) { return diff --git a/core/zerr/wrap.go b/core/zerr/wrap.go index 168b547..975360e 100644 --- a/core/zerr/wrap.go +++ b/core/zerr/wrap.go @@ -7,19 +7,20 @@ import ( "gitlab.bareksa.com/backend/zen/core/zoptions" ) -type WrapInitialInput struct { - Error error +type WrapInput struct { + Errors []error Message string PC uintptr Time time.Time Logger Logger Notifier Notifier + Details []any } type Wrapper interface { // Wrap creates a zerr.Error with inputs // generated by global entry points. - Wrap(input WrapInitialInput) Error + Wrap(input WrapInput) Error } type Logger interface { @@ -30,12 +31,35 @@ type Notifier interface { NotifyError(ctx context.Context, err Error, opts ...zoptions.NotifyOption) } -type WrapperFunc func(input WrapInitialInput) Error +type WrapperFunc func(input WrapInput) Error -func (wr WrapperFunc) Wrap(input WrapInitialInput) Error { +func (wr WrapperFunc) Wrap(input WrapInput) Error { return wr(input) } -var DefaultWrapper Wrapper = WrapperFunc(func(input WrapInitialInput) Error { - panic("not implemented") +var DefaultWrapper Wrapper = WrapperFunc(func(input WrapInput) Error { + e := &Err{ + message: input.Message, + errs: input.Errors, + caller: input.PC, + time: input.Time, + logger: input.Logger, + notifier: input.Notifier, + details: input.Details, + } + e.sequence = &sequence{err: e} + for _, err := range input.Errors { + if err, ok := err.(Error); ok { + err.Sequence().WrapBy(e.sequence) + } + } + return e }) + +func Wrap(input WrapInput) Error { + return DefaultWrapper.Wrap(input) +} + +func SetDefaultWrapper(w Wrapper) { + DefaultWrapper = w +} diff --git a/go.mod b/go.mod index 7b11616..7789334 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,8 @@ require ( connectrpc.com/connect v1.16.2 connectrpc.com/grpcreflect v1.2.0 github.com/fatih/color v1.17.0 + github.com/mattn/go-isatty v0.0.20 + github.com/pborman/indent v1.2.1 github.com/tidwall/pretty v1.2.1 golang.org/x/net v0.23.0 google.golang.org/protobuf v1.34.2 @@ -13,8 +15,6 @@ require ( require ( github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/pborman/indent v1.2.1 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect )