// ------------------------------------------------------------------------ // Project active2 // Active Thing (activething.com) git.activething.com/go // // File name error_text_formatter.go // Created by DEV // Modified 27/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 errs import ( "fmt" "reflect" "sync" "time" ) type ( FmtText struct { FmtTextAtt indent string traceFmt func(s *FmtTextState, t StackTrace) providers sync.Map } FmtTextFnc func(f *FmtText) ) func NewFmtText(options ...FmtTextFnc) *FmtText { return applyFmtTextOptions(&FmtText{ FmtTextAtt: FmtTextAttIndent | FmtTextAttVerbose | FmtTextAttTrace | FmtTextAttColors, indent : " ", traceFmt : formatStackTrace , }, options...) } func (f *FmtText) Format (err error) []byte { if err == nil { return nil } st := newFmtTextState(f) dn := false defer func(d *bool, s *FmtTextState) { s.Buffer.Reset() if !(*d) { e := NewErrPanic(recover()) if e != nil { s.WriteFieldString("panic",e.Error() ) } } }(&dn,st) f.formatAll(st,err) if f.AddStackTrace() { if tc := UnwrapStackTrace(err); st != nil { st.WriteString("\nStack Trace\n") f.traceFmt(st, tc.StackTrace()) } } dn=true return st.Buffer.Bytes() } func (f *FmtText) formatAll (s *FmtTextState, err error) { ls := UnwrapAll(err) for _,e := range ls { f.format(s,e) } if c := UnwrapCause(err); c != nil { s.WriteString("\n") s.IncLevel() if f.Verbose() { f.formatAll(s, c.Cause()) } else { f.format(s,c.Cause()) } s.DecLevel() } } func (f *FmtText) format (s *FmtTextState, err error) { rt := reflect.TypeOf(err) if rt.Kind() == reflect.Pointer { rt = rt.Elem() err = reflect.ValueOf(err).Elem().Interface().(error) if err == nil { return } } f.getFmtTextProvider(rt).Format(s,err) } func (f *FmtText) getFmtTextProvider(rt reflect.Type) FmtTextProvider { pt := rt.Kind() == reflect.Pointer if pt { rt = rt.Elem() } if fp,ok := f.providers.Load(rt); ok { return fp.(FmtTextProvider) } var wg sync.WaitGroup var np FmtTextProvider wg.Add(1) fp,ld := f.providers.LoadOrStore(rt, FmtTextProviderFnc(func (s *FmtTextState, err error){ wg.Wait() np.Format(s,err) })) if ld { return fp.(FmtTextProvider) } np = f.buildFmtTextProvider(rt) wg.Done() f.providers.Store(rt,np) return np } func (f *FmtText) buildFmtTextProvider (rtype reflect.Type) FmtTextProvider { if rtype.Kind() == reflect.Pointer { rtype = rtype.Elem() } switch rtype { case G.errCodeRT : return FmtTextProviderFnc(formatErrCode ) case G.errCauseRT: return FmtTextProviderFnc(formatErrCause) case G.errTraceRT: return FmtTextProviderFnc(formatErrTrace) case G.errPanicRT: return FmtTextProviderFnc(formatErrPanic) case G.errInfoRT : return FmtTextProviderFnc(formatErrInfo ) default : return FmtTextProviderFnc(formatErrAny) } } func formatErrCode (s *FmtTextState, e error) { s.WriteFieldString("error-code",e.(ErrCode).Code()) } func formatErrPanic (s *FmtTextState, e error ){ s.WriteFieldString("error-panic",e.(ErrPanic).Error()) } func formatErrTrace (s *FmtTextState, e error) { s.WriteFieldString("error-Trace",e.(ErrTrace).Error()) } func formatErrCause (s *FmtTextState, e error) { s.WriteFieldString("error-cause",e.(ErrCause).Error()) } func formatErrInfo (s *FmtTextState, e error) { ei := e.(ErrInfo) s.WriteFieldString("message" ,ei.Message()) s.WriteFieldString("info" ,ei.Info ()) if s.Level() == 0 { s.WriteFieldString( "date-time", ei.Date().Format(time.RFC822Z)) ei.MapMetas(func(n string, v any) bool { s.WriteFieldString(n,fmt.Sprintf("%v",v)) return true }) } } func formatErrAny (s *FmtTextState, err error) { s.WriteFieldString("error",err.Error()) } func formatStackTrace(s *FmtTextState, t StackTrace) { for _,f := range t.Frames() { s.Buffer.WriteString(f.Package) s.Buffer.WriteByte(':') s.Buffer.WriteString(f.FuncName) s.Buffer.WriteByte('\n') s.Buffer.WriteByte('\t') s.Buffer.WriteString(f.File) s.Buffer.WriteByte(':') s.Buffer.WriteString(fmt.Sprintf("%d\n",f.LineNumber)) if sc,er := f.SourceLine(); er == nil { s.Buffer.WriteString(ColorMagenta.Sprint(sc)) //s.Buffer.WriteString(fmt.Sprintf("\t\x1b[%dm%s\x1b[0m\b\n", uint8(Green), sc)) //s.Buffer.WriteByte('\t') //s.Buffer.WriteString(sc) //s.Buffer.WriteByte('\n') } } }