From 659c46a58cbe0f5a448af0722215449fbb28c6b4 Mon Sep 17 00:00:00 2001 From: DEV Date: Fri, 19 Apr 2024 00:20:05 +0200 Subject: [PATCH] seguims con los errores --- app/main.go | 2 +- core/atypes/astrings/transformer.go | 42 +++++ .../{transform.go => transformers.go} | 14 +- core/errs/fmtr/ftext/active.go | 69 +++++--- core/errs/fmtr/ftext/builder.go | 149 ++++++++++++------ core/errs/fmtr/ftext/fallback.go | 31 ++++ core/errs/fmtr/ftext/formatter.go | 62 ++++++-- core/errs/fmtr/ftext/layouts.go | 36 +++-- core/errs/fmtr/ftext/option.go | 46 +++--- core/errs/stack/active.go | 22 +++ 10 files changed, 351 insertions(+), 122 deletions(-) create mode 100644 core/atypes/astrings/transformer.go rename core/atypes/astrings/{transform.go => transformers.go} (85%) create mode 100644 core/errs/fmtr/ftext/fallback.go diff --git a/app/main.go b/app/main.go index 0054904..e2417d2 100644 --- a/app/main.go +++ b/app/main.go @@ -81,11 +81,11 @@ func main() { e4 := &MiCustomErr{} e5 := &MiCustomErr{} + //me := errs.NewErrTrace(errs.NewErrMulti(e3,e4,e5,ne).WrapWithInfo(errs.ErrInfoWithMessage("Es0")),0) me := errs.NewErrMulti(e3,e4,e5,ne).WrapWithInfo(errs.ErrInfoWithMessage("Es0")) - fmt.Print(errs.ToFormat(me,nil)) diff --git a/core/atypes/astrings/transformer.go b/core/atypes/astrings/transformer.go new file mode 100644 index 0000000..1da45fe --- /dev/null +++ b/core/atypes/astrings/transformer.go @@ -0,0 +1,42 @@ +// ------------------------------------------------------------------------ +// Project active2 +// Active Thing (activething.com) git.activething.com/go +// +// File name string_adapter.go +// Created by DEV +// Modified 31/03/2024 +// +// Copyright 2024 activething.com +// ------------------------------------------------------------------------ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ------------------------------------------------------------------------ + +package astrings + + + +type ( + + + Transformer interface { + Transform (value string) string + } + + + TransformFnc func (value string) string + +) + + +func (f TransformFnc) Transform (value string) string { + return f(value) } \ No newline at end of file diff --git a/core/atypes/astrings/transform.go b/core/atypes/astrings/transformers.go similarity index 85% rename from core/atypes/astrings/transform.go rename to core/atypes/astrings/transformers.go index 9c46e69..febbdcb 100644 --- a/core/atypes/astrings/transform.go +++ b/core/atypes/astrings/transformers.go @@ -2,9 +2,9 @@ // Project active2 // Active Thing (activething.com) git.activething.com/go // -// File name string_adapter.go +// File name transformers.go // Created by DEV -// Modified 31/03/2024 +// Modified 18/04/2024 // // Copyright 2024 activething.com // ------------------------------------------------------------------------ @@ -25,24 +25,24 @@ package astrings -type ( - Transform func (string) string +type ( - Transformers []Transform + Transformers []Transformer ) -func (a Transformers) Apply (value string) string { +func (a Transformers) Transform (value string) string { if len(a) == 0 { return value } s := value for _,f := range a { - s = f(s) + s = f.Transform(s) } return s } + diff --git a/core/errs/fmtr/ftext/active.go b/core/errs/fmtr/ftext/active.go index c443a80..0afe465 100644 --- a/core/errs/fmtr/ftext/active.go +++ b/core/errs/fmtr/ftext/active.go @@ -25,7 +25,10 @@ package ftext import ( "ansi" + "atypes/astrings" + "errs/stack" "fmt" + "strings" "sync" ) @@ -44,47 +47,73 @@ func DefaultFormatter () *TextFormatter { g.buildDefault.Do(func() { g.formatter = NewTextFormatter( + + WithOptions(0,AddFields), + + WithNameTransformers( + astrings.TransformFnc(func (s string) string { + return astrings.ToKebab(s) }), + astrings.TransformFnc(func (s string) string { + return astrings.Pad(s,8,' ',true) }), + ), + + WithSectionsFormat(func(c *Sections){ c.Main.Set(0, func (b *TextBuilder){ - c := ansi.Code(0).SetFgColor(ansi.ColorHRed).SetStyle(ansi.StyleBold) - b.AddText(c.Sprint("# ERROR #####################################################\n")) - }, + b.AddTextAnsi("# ERROR #####################################################\n",ansi.Code(0).SetFgColor(ansi.ColorHRed).SetStyle(ansi.StyleBold)) }, nil) c.Head.Set(ansi.Code(0).SetFgColor(ansi.ColorHRed),nil,nil) c.Fields.Set(ansi.Code(0).SetFgColor(ansi.ColorGreen), - func(b *TextBuilder) { - - }, + nil, nil) c.Causes.Set(ansi.Code(0).SetFgColor(ansi.ColorCyan), func(b *TextBuilder){ - a := ansi.Code(0).SetStyle(ansi.StyleBold) - b.AddText(a.Sprint("* Prev Causes ***********************************************\n")) - }, - func(b *TextBuilder){ - a := ansi.Code(0).SetStyle(ansi.StyleBold) - b.AddText(a.Sprint("*************************************************************\n")) - }) + b.AddTextAnsi("\n* Prev Causes ***********************************************\n",ansi.Code(0).SetStyle(ansi.StyleBold)) }, + nil) c.Cause.Set(ansi.Code(0).SetFgColor(ansi.ColorCyan), func(b *TextBuilder) { - b.AddText(fmt.Sprintf("-- %d --------------------------------------------------------\n",b.Level())) - }, + b.AddTextAnsi(fmt.Sprintf("-- %d --------------------------------------------------------\n",b.Level()),0) }, nil) - c.Trace.Set(ansi.Code(0).SetFgColor(ansi.ColorHYellow), + c.Trace.Set(ansi.Code(0).SetFgColor(ansi.ColorHYellow).SetBgColor(ansi.ColorBlack), func(b *TextBuilder){ - a := b.ansiC.SetStyle(ansi.StyleBold) - b.AddText(a.Sprint("* Stack Trace ***********************************************\n")) - }, + b.AddTextAnsi("\n# Stack Trace ###############################################\n",b.ansiC.SetStyle(ansi.StyleBold).SetFgColor(ansi.ColorHYellow)) }, nil) }), ) }) return g.formatter -} \ No newline at end of file +} + + + +func defaultFrameBuilder (builder *TextBuilder, frame stack.Frame, num int) { + if strings.Index(frame.Package,"runtime") != -1 { + return + } + + var c1, c2, c3, c4 ansi.Code + if builder.Options().ShowAnsi(){ + c1 = c1.SetFgColor(ansi.ColorBlue)//.SetBgColor(ansi.ColorHBlack) + c2 = c2.SetFgColor(ansi.ColorMagenta) + c3 = c3.SetFgColor(ansi.ColorWhite) + c4 = c3.SetFgColor(ansi.ColorHWhite) + } + + builder.AddText(c4.Sprintf(" %d %s.%s \n",num,frame.Package, frame.FuncName)) + builder.AddText(c2.Sprintf(" %.90s:%d\n", frame.File, frame.LineNumber)) + + f, e := frame.SourceLine() + if e == nil { + builder.AddTextAnsi(fmt.Sprintf(" %s\n", f),c3) + } else { + builder.AddTextAnsi(fmt.Sprintf(" error %s\n", e.Error()),c3) + } +} + diff --git a/core/errs/fmtr/ftext/builder.go b/core/errs/fmtr/ftext/builder.go index f5ae9d5..9a75225 100644 --- a/core/errs/fmtr/ftext/builder.go +++ b/core/errs/fmtr/ftext/builder.go @@ -28,6 +28,7 @@ import ( "bytes" "errors" "errs/fmtr" + "errs/stack" "fmt" "reflect" "strings" @@ -54,9 +55,9 @@ type ( func newTextBuilder(formatter *TextFormatter) *TextBuilder { return &TextBuilder{ - f : formatter, - } -} + f : formatter, + ansiC: formatter.layout.ansiC }} + func (s *TextBuilder) Indent() string { @@ -68,11 +69,24 @@ func (s *TextBuilder) Level () int { func (s *TextBuilder) Ansi () ansi.Code { return s.ansiC } +func (s *TextBuilder) Options () Option { + return s.f.Option +} + func (s *TextBuilder) AddText (text string) { - s.b.WriteString(s.ansiC.String()) + s.b.WriteString(text) } + +func (s *TextBuilder) AddTextAnsi (text string, ansiC ansi.Code) { + if s.f.ShowAnsi() { + if ansiC == 0 { + ansiC = s.ansiC + } + s.b.WriteString(ansiC.String()) + defer s.b.WriteString(ansiC.End()) + } + s.b.WriteString(text) - s.b.WriteString(s.ansiC.End()) } @@ -81,34 +95,35 @@ func (s *TextBuilder) AddFieldAny(name string, value any ) { if value == nil && s.f.NotShowEmpty() { return } + s.writeField(name,fmt.Sprintf("%v",value),false) } func (s *TextBuilder) AddFieldInt(name string, value int ) { - s.writeField(name,fmt.Sprintf(s.f.layouts.intLayout,value))} + s.writeField(name,fmt.Sprintf(s.f.layout.IntLayout,value),false)} func (s *TextBuilder) AddFieldBool(name string, value bool ) { - s.writeField(name,fmt.Sprintf(s.f.layouts.boolLayout)) } + s.writeField(name,fmt.Sprintf(s.f.layout.BoolLayout),false) } func (s *TextBuilder) AddFieldString(name string, value string ) { if len(value) == 0 && s.f.NotShowEmpty() { return } - s.writeField(name,value) } + s.writeField(name,value,false) } func (s *TextBuilder) AddFieldFloat(name string, value float64) { - s.writeField(name,fmt.Sprintf(s.f.layouts.floatLayout,value)) } + s.writeField(name,fmt.Sprintf(s.f.layout.FloatLayout,value),false) } func (s *TextBuilder) AddFieldTime(name string, value time.Time) { if value.IsZero() && s.f.NotShowEmpty() { return } - s.writeField(name,value.Format(s.f.layouts.timeLayout)) } + s.writeField(name,value.Format(s.f.layout.TimeLayout),false) } func (s *TextBuilder) AddFieldDate(name string, value time.Time) { if value.IsZero() && s.f.NotShowEmpty() { return } - s.writeField(name,value.Format(s.f.layouts.dateLayout)) } + s.writeField(name,value.Format(s.f.layout.DateLayout),false) } @@ -118,9 +133,9 @@ func (s *TextBuilder) Build(errCode, errType string, fbuilder fmtr.BuildFieldFnc if s.level == 0 { s.f.sections.Head.begin(s) } - s.writeField(fmtr.FieldNameError,errCode) - if s.f.ShowType() { - s.writeField(fmtr.FieldNameType, errType) + s.writeField(fmtr.FieldNameError,errCode,true) + if s.f.AddType() { + s.writeField(fmtr.FieldNameType, errType,true) } if s.level == 0 { s.f.sections.Head.end(s) @@ -149,17 +164,18 @@ func (s *TextBuilder) format (err error) (fe error) { s.f.sections.Fields.end(s) } - if s.f.ShowCause() { + if s.f.AddCause() { s.f.sections.Causes.begin(s) if fe = s.addCause(err); fe != nil { - //todo + s.f.onCauseError(s,fe) } s.f.sections.Causes.end(s) } - if s.f.ShowTrace() { - s.f.sections.Trace.begin(s) - s.f.sections.Trace.end(s) + if s.f.AddTrace() { + if fe = s.addTrace(err); fe != nil { + s.f.onTraceError(s,fe) + } } s.f.sections.Main.end(s) @@ -186,22 +202,26 @@ func (s *TextBuilder) addError(err error) (fe error) { eb := fmtr.GetErrorTypeBuilder(rt) if eb != nil { eb.BuildError(s,err) - return + } else { + eb = fmtr.MakeErrorTypeBuilder(s, err) + fmtr.SetErrorTypeBuilder(rt, eb) } - - eb = fmtr.MakeErrorTypeBuilder(s,err) - fmtr.SetErrorTypeBuilder(rt,eb) } - if !s.unwrap { - s.unwrap = true - } - if fe = s.addError(errors.Unwrap(err)); fe != nil { - //todo - return fe - } - s.unwrap = false + if ne := errors.Unwrap(err); ne != nil { + if !s.unwrap { + s.unwrap = true + defer func() { + s.unwrap = false + }() + } + + if fe = s.addError(ne); fe != nil { + s.f.onFormatError(s,fe) + return fe + } + } return fe } @@ -210,17 +230,21 @@ func (s *TextBuilder) addError(err error) (fe error) { func (s *TextBuilder) addCause(err error) (fe error) { + if err == nil { + return + } + ls := getCauses(err) for _,ce := range ls { s.incLevel() s.f.sections.Cause.begin(s) + if fe = s.addError(ce); fe != nil { - //todo - continue - } - if fe = s.addCause(ce); fe != nil { - //todo + s.f.onCauseError(s,fe) + } else if fe = s.addCause(ce); fe != nil { + s.f.onCauseError(s,fe) } + s.f.sections.Cause.end(s) s.decLevel() } @@ -229,6 +253,7 @@ func (s *TextBuilder) addCause(err error) (fe error) { + func getCauses (err error) []error { ne := err for ne != nil { @@ -251,6 +276,33 @@ func getCauses (err error) []error { } +func (s *TextBuilder) addTrace (err error) (fe error) { + st := stack.UnwrapTrace(err,true) + if st == nil { + return + } + + dn := false + s.f.sections.Trace.begin(s) + defer func(d *bool, e *error){ + if !(*d) { + if er := recover(); er != nil { + if ne, is := er.(error); is { + fe = ne + } else { + fe = fmt.Errorf("%#v", er) + } + } + } + s.f.sections.Trace.end(s) + }(&dn,&fe) + + for n,f := range st.Frames() { + s.f.frameBuilder(s,f,n) + } + dn = true + return +} @@ -258,24 +310,29 @@ func (s *TextBuilder) setAnsi(ansiC ansi.Code) { if ansiC != 0 { s.ansiC = ansiC } else { - s.ansiC = s.f.ansiC + s.ansiC = s.f.layout.ansiC } } -func (s *TextBuilder) writeField (name string, value string) { - s.numFields++ +func (s *TextBuilder) writeField (name string, value string, head bool) { + if !head { + if !s.f.AddFields() { + return + } + s.numFields++ + } - n := s.f.nameTrans.Apply(name) - v := s.f.valueTrans.Apply(value) + n := s.f.nameTrans.Transform(name) + v := s.f.valueTrans.Transform(value) if s.ansiC != 0 { s.b.WriteString(s.ansiC.String()) } s.b.WriteString(s.indent) s.b.WriteString(n) - s.b.WriteString(s.f.sep) + s.b.WriteString(s.f.layout.sep) s.b.WriteString(v) s.b.WriteByte('\n') @@ -289,15 +346,15 @@ func (s *TextBuilder) writeField (name string, value string) { func (s *TextBuilder) incLevel () { s.level++ - if s.f.ShowIndent() { - s.indent = strings.Repeat(s.f.indent,s.level) + if s.f.UseIndent() { + s.indent = strings.Repeat(s.f.layout.indent,s.level) } } func (s *TextBuilder) decLevel () { s.level-- - if s.f.ShowIndent() && s.level > 0 { - s.indent = strings.Repeat(s.f.indent,s.level) + if s.f.UseIndent() && s.level > 0 { + s.indent = strings.Repeat(s.f.layout.indent,s.level) } } diff --git a/core/errs/fmtr/ftext/fallback.go b/core/errs/fmtr/ftext/fallback.go new file mode 100644 index 0000000..93b603d --- /dev/null +++ b/core/errs/fmtr/ftext/fallback.go @@ -0,0 +1,31 @@ +// ------------------------------------------------------------------------ +// Project active2 +// Active Thing (activething.com) git.activething.com/go +// +// File name fallback.go +// Created by DEV +// Modified 18/04/2024 +// +// Copyright 2024 activething.com +// ------------------------------------------------------------------------ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ------------------------------------------------------------------------ + +package ftext + + +type ( + + + ErrorFallbackFnc func (builder *TextBuilder, err error) +) \ No newline at end of file diff --git a/core/errs/fmtr/ftext/formatter.go b/core/errs/fmtr/ftext/formatter.go index 66cb3c5..f57410c 100644 --- a/core/errs/fmtr/ftext/formatter.go +++ b/core/errs/fmtr/ftext/formatter.go @@ -24,7 +24,6 @@ package ftext import ( - "ansi" "atypes/astrings" "errs/stack" ) @@ -33,18 +32,18 @@ type ( TextFormatter struct { - option + Option - ansiC ansi.Code - indent string - sep string - - layouts layouts + layout Layout sections Sections nameTrans astrings.Transformers valueTrans astrings.Transformers + onFormatError ErrorFallbackFnc + onTraceError ErrorFallbackFnc + onCauseError ErrorFallbackFnc + frameBuilder func (builder *TextBuilder, frame stack.Frame, num int) } @@ -54,10 +53,12 @@ type ( func NewTextFormatter(options ...TextFormatterFnc) *TextFormatter { fm := &TextFormatter{ - option : optionsDefault, - indent : " ", - sep : " : ", - layouts : *newLayouts(), + Option : optionsDefault, + frameBuilder : defaultFrameBuilder, + layout : *newLayouts(), + onFormatError : func(b *TextBuilder, e error) { }, + onTraceError : func(b *TextBuilder, e error) { }, + onCauseError : func(b *TextBuilder, e error) { }, } for _,o := range options { o(fm) @@ -79,9 +80,46 @@ func (f *TextFormatter) FormatError (err error) []byte { } +func WithErrorFallbacks (format, cause, trace ErrorFallbackFnc) TextFormatterFnc { + return func(f *TextFormatter) { + if format == nil { + f.onFormatError = format + } + if cause == nil { + f.onCauseError = cause + } + if trace == nil { + f.onTraceError = trace + } + } +} + + +func WithOptions (set, unset Option) TextFormatterFnc { + return func(f *TextFormatter) { + f.Option |= set + f.Option &^= unset } } + + +func WithNameTransformers(transformer ...astrings.Transformer) TextFormatterFnc { + return func(f *TextFormatter) { + f.nameTrans = append(f.nameTrans, transformer ...) } } +func WithValueTransformers(transformer ...astrings.Transformer) TextFormatterFnc { + return func(f *TextFormatter) { + f.valueTrans = append(f.nameTrans, transformer ...) } } func WithSectionsFormat(config SectionsFnc) TextFormatterFnc { return func(f *TextFormatter) { - config(&f.sections) } } \ No newline at end of file + config(&f.sections) } } + + +func WithLayouts (config LayoutFnc) TextFormatterFnc { + return func(f *TextFormatter) { + config(&f.layout) } } + + +func WithFrameBuilder (builderFn func (builder *TextBuilder, frame stack.Frame, num int)) TextFormatterFnc { + return func(f *TextFormatter) { + f.frameBuilder = builderFn } } \ No newline at end of file diff --git a/core/errs/fmtr/ftext/layouts.go b/core/errs/fmtr/ftext/layouts.go index 4e09b8c..326d9a8 100644 --- a/core/errs/fmtr/ftext/layouts.go +++ b/core/errs/fmtr/ftext/layouts.go @@ -2,7 +2,7 @@ // Project active2 // Active Thing (activething.com) git.activething.com/go // -// File name layouts.go +// File name Layout.go // Created by DEV // Modified 13/04/2024 // @@ -24,28 +24,38 @@ package ftext import ( + "ansi" "time" ) type ( - layouts struct { - timeLayout string - dateLayout string - intLayout string - floatLayout string - boolLayout string + Layout struct { + ansiC ansi.Code + indent string + sep string + + TimeLayout string + DateLayout string + IntLayout string + FloatLayout string + BoolLayout string } + + LayoutFnc func (layouts *Layout) ) -func newLayouts () *layouts { - return &layouts{ - timeLayout : time.RFC822Z, - dateLayout : time.RFC822Z, - intLayout : "%d", - floatLayout : "%-10s: %.2f", +func newLayouts () *Layout { + return &Layout{ + indent : " ", + sep : " : ", + + TimeLayout: time.RFC822Z, + DateLayout: time.RFC822Z, + IntLayout: "%d", + FloatLayout: "%-10s: %.2f", } } diff --git a/core/errs/fmtr/ftext/option.go b/core/errs/fmtr/ftext/option.go index 61f433a..8d2b947 100644 --- a/core/errs/fmtr/ftext/option.go +++ b/core/errs/fmtr/ftext/option.go @@ -26,51 +26,51 @@ package ftext const ( - ShowWrapped option = 1 << iota - ShowType + AddWrapped Option = 1 << iota + AddType + AddFields + AddCause + AddTrace ShowAnsi - ShowIndent - ShowFields - ShowCause - ShowTrace + UseIndent NotShowEmpty - optionsDefault = ShowWrapped | ShowType | ShowAnsi | ShowIndent | ShowFields | ShowCause | ShowTrace | NotShowEmpty + optionsDefault = AddWrapped | AddType | ShowAnsi | UseIndent | AddFields | AddCause | AddTrace | NotShowEmpty ) type ( - option uint8 + Option uint8 ) -func (s option) SetDefaults () option { +func (s Option) SetDefaults () Option { return optionsDefault } -func (s option) ShowWrapped () bool { - return s&ShowWrapped !=0 } +func (s Option) AddWrapped() bool { + return s&AddWrapped !=0 } -func (s option) ShowType () bool { - return s&ShowType !=0 } +func (s Option) AddType() bool { + return s&AddType !=0 } -func (s option) ShowAnsi () bool { +func (s Option) ShowAnsi () bool { return s&ShowAnsi !=0 } -func (s option) ShowIndent () bool { - return s&ShowIndent !=0 } +func (s Option) UseIndent() bool { + return s&UseIndent !=0 } -func (s option) ShowFields() bool { - return s&ShowFields !=0 } +func (s Option) AddFields() bool { + return s&AddFields !=0 } -func (s option) ShowCause () bool { - return s&ShowCause !=0 } +func (s Option) AddCause() bool { + return s&AddCause !=0 } -func (s option) ShowTrace () bool { - return s&ShowTrace !=0 } +func (s Option) AddTrace() bool { + return s&AddTrace !=0 } -func (s option) NotShowEmpty() bool { +func (s Option) NotShowEmpty() bool { return s&NotShowEmpty !=0 } diff --git a/core/errs/stack/active.go b/core/errs/stack/active.go index 5f5a62a..36b4d6b 100644 --- a/core/errs/stack/active.go +++ b/core/errs/stack/active.go @@ -51,8 +51,30 @@ func UnwrapTrace(err error, cause bool) Trace { if cs, is := e.(interface{ Cause() error }); is { UnwrapTrace(cs.Cause(), cause) } + + if cs, is := e.(interface{ Causes() []error}); is { + if tc := unwrapTraceMulti(cs.Causes()); tc != nil { + return tc + } + } + if cs, is := e.(interface{ Unwrap() []error}); is { + if tc := unwrapTraceMulti(cs.Unwrap()); tc != nil { + return tc + } + } + e = errors.Unwrap(e) } } return nil +} + + +func unwrapTraceMulti (ers []error) Trace { + for _,e := range ers { + if tc := UnwrapTrace(e,true); tc != nil { + return tc + } + } + return nil } \ No newline at end of file