zerr: sequence: changed into interface for future proofing

This commit is contained in:
Tigor Hutasuhut 2024-08-27 17:08:52 +07:00
parent dba226c1c1
commit ab159d9426
6 changed files with 89 additions and 33 deletions

View file

@ -27,7 +27,7 @@ type Err struct {
id string id string
logger Logger logger Logger
notifier Notifier notifier Notifier
sequence *Sequence sequence Sequence
} }
// Join implements Error. // 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. // Sequence returns the state of the error sequence.
func (mu *Err) Sequence() *Sequence { func (mu *Err) Sequence() Sequence {
return mu.sequence return mu.sequence
} }

View file

@ -18,9 +18,11 @@ func (er *Err) WriteBuilder(w io.Writer) {
return return
} }
var previous string var previous string
if err := er.sequence.Outer(); err != nil && !err.IsMulti() { if parent := er.sequence.Parent(); parent != nil {
if err := parent.Error(); err != nil && !err.IsMulti() {
previous = err.GetMessage() previous = err.GetMessage()
} }
}
current := strings.TrimPrefix(er.message, previous) current := strings.TrimPrefix(er.message, previous)
if current != "" { if current != "" {
if previous != "" { if previous != "" {

View file

@ -85,7 +85,9 @@ type Error interface {
Notify(ctx context.Context, opts ...zoptions.NotifyOption) Error Notify(ctx context.Context, opts ...zoptions.NotifyOption) Error
// Sequence returns the state of the error sequence. // 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 returns true if the error is a multiple error.
IsMulti() bool IsMulti() bool

View file

@ -9,24 +9,50 @@ import "iter"
// When wrapping errors, the wrapping error // When wrapping errors, the wrapping error
// will be the parent of the wrapped Error // will be the parent of the wrapped Error
// if the wrapped Error is a zerr.Error. // if the wrapped Error is a zerr.Error.
type Sequence struct { type Sequence interface {
parent *Sequence // WrapBy prepends the next Sequence to the current Sequence.
children []*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 err Error
} }
// WrapBy prepends the next Sequence to the current Sequence. func (se *sequence) WrapBy(parent Sequence) {
func (se *Sequence) WrapBy(parent *Sequence) { parent.AppendChild(se)
parent.children = append(parent.children, se)
se.parent = parent se.parent = parent
} }
// Outer returns the error that wraps this Error. func (se *sequence) AppendChild(child Sequence) {
// Return nil if this Error is not wrapped se.children = append(se.children, child)
// or wrapping outer is not a zerr.Error. }
func (se *Sequence) Outer() Error {
func (se *sequence) Parent() Sequence {
if se.parent != nil { if se.parent != nil {
return se.parent.err return se.parent
} }
return nil return nil
} }
@ -34,21 +60,23 @@ func (se *Sequence) Outer() Error {
// Root returns the outermost Error in the sequence. // Root returns the outermost Error in the sequence.
// //
// Returns self if there is no outermost. // Returns self if there is no outermost.
func (se *Sequence) Root() *Sequence { func (se *sequence) Root() Sequence {
root := se var root Sequence = se
for root.parent != nil { for {
root = root.parent if root.Parent() == nil {
}
return root return root
}
root = root.Parent()
}
} }
// IsRoot checks if the Error is the outermost Error in the sequence. // 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 return se.parent == nil
} }
// Error returns the Error in the sequence. // Error returns the Error in the sequence.
func (se *Sequence) Error() Error { func (se *sequence) Error() Error {
return se.err return se.err
} }
@ -64,7 +92,7 @@ func (se *Sequence) Error() Error {
// // e is zerr.Error // // e is zerr.Error
// // do something with e // // do something with e
// } // }
func (se *Sequence) Iter() iter.Seq[Error] { func (se *sequence) Iter() iter.Seq[Error] {
return func(yield func(Error) bool) { return func(yield func(Error) bool) {
if se.err != nil && !yield(se.err) { if se.err != nil && !yield(se.err) {
return return

View file

@ -7,19 +7,20 @@ import (
"gitlab.bareksa.com/backend/zen/core/zoptions" "gitlab.bareksa.com/backend/zen/core/zoptions"
) )
type WrapInitialInput struct { type WrapInput struct {
Error error Errors []error
Message string Message string
PC uintptr PC uintptr
Time time.Time Time time.Time
Logger Logger Logger Logger
Notifier Notifier Notifier Notifier
Details []any
} }
type Wrapper interface { type Wrapper interface {
// Wrap creates a zerr.Error with inputs // Wrap creates a zerr.Error with inputs
// generated by global entry points. // generated by global entry points.
Wrap(input WrapInitialInput) Error Wrap(input WrapInput) Error
} }
type Logger interface { type Logger interface {
@ -30,12 +31,35 @@ type Notifier interface {
NotifyError(ctx context.Context, err Error, opts ...zoptions.NotifyOption) 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) return wr(input)
} }
var DefaultWrapper Wrapper = WrapperFunc(func(input WrapInitialInput) Error { var DefaultWrapper Wrapper = WrapperFunc(func(input WrapInput) Error {
panic("not implemented") 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
}

4
go.mod
View file

@ -6,6 +6,8 @@ require (
connectrpc.com/connect v1.16.2 connectrpc.com/connect v1.16.2
connectrpc.com/grpcreflect v1.2.0 connectrpc.com/grpcreflect v1.2.0
github.com/fatih/color v1.17.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 github.com/tidwall/pretty v1.2.1
golang.org/x/net v0.23.0 golang.org/x/net v0.23.0
google.golang.org/protobuf v1.34.2 google.golang.org/protobuf v1.34.2
@ -13,8 +15,6 @@ require (
require ( require (
github.com/mattn/go-colorable v0.1.13 // indirect 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/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect
) )