You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
270 lines
5.0 KiB
270 lines
5.0 KiB
// ------------------------------------------------------------------------
|
|
// Project active2
|
|
// Active Thing (activething.com) git.activething.com/go
|
|
//
|
|
// File name state.go
|
|
// Created by DEV
|
|
// Modified 12/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 fmx
|
|
|
|
import (
|
|
"ansi"
|
|
"bytes"
|
|
"errors"
|
|
"errs/err_fmt"
|
|
"errs/stack"
|
|
"fmt"
|
|
"reflect"
|
|
)
|
|
|
|
const (
|
|
|
|
secHead uint8 = iota
|
|
secFields
|
|
secCause
|
|
)
|
|
|
|
type (
|
|
|
|
|
|
state struct {
|
|
f *TextFmt
|
|
b bytes.Buffer
|
|
|
|
fDone bool // se ha añadido la seccion de fields
|
|
curSec uint8 // actual seccion del formato
|
|
ansi ansi.Code
|
|
}
|
|
)
|
|
|
|
|
|
func newState (f *TextFmt) *state {
|
|
return &state { f : f }}
|
|
|
|
|
|
func (s *state) reset () *state {
|
|
s.curSec = 0
|
|
s.ansi = 0
|
|
s.b.Reset()
|
|
return s
|
|
}
|
|
|
|
|
|
func (s *state) writeError(err error, addUnwrap, addCause, addTrace bool, level int) (fe error) {
|
|
|
|
d := false
|
|
defer func(st *state, dn *bool, er *error){
|
|
if !(*dn) {
|
|
if rc := recover(); fe != nil {
|
|
ec,is := rc.(error)
|
|
if !is {
|
|
ec = fmt.Errorf("%#v",rc)
|
|
}
|
|
*er = *err_fmt.NewErrBuilderFormat(ec,reflect.TypeOf(er),true)
|
|
}
|
|
}
|
|
}(s,&d,&fe)
|
|
|
|
if fe,is := err.(err_fmt.Buildable); is {
|
|
fe.Build(s)
|
|
} else {
|
|
rt := reflect.TypeOf(err)
|
|
if rt.Kind() == reflect.Pointer {
|
|
rt = rt.Elem()
|
|
}
|
|
eb := err_fmt.GetErrorTypeBuilder(rt)
|
|
if eb != nil {
|
|
eb.BuildError(s,err)
|
|
} else {
|
|
eb := err_fmt.MakeErrorTypeBuilder(s,err)
|
|
err_fmt.SetErrorTypeBuilder(rt,eb)
|
|
}
|
|
}
|
|
if addUnwrap {
|
|
return fe
|
|
}
|
|
|
|
s.writeUnwrap(err,level)
|
|
|
|
|
|
if addCause {
|
|
s.writeCause(err,level)
|
|
}
|
|
|
|
if addTrace {
|
|
s.writeTrace (err)
|
|
}
|
|
|
|
d = true
|
|
return nil
|
|
}
|
|
|
|
|
|
|
|
func (s *state) writeUnwrap(err error, level int) {
|
|
uw := errors.Unwrap(err)
|
|
for uw != nil {
|
|
if ue := s.writeError(uw,true,false,false, level); ue != nil {
|
|
// llamamos al fallback del formateador
|
|
s.AddText(s.f.fbOnUnwrap(uw))
|
|
}
|
|
uw = errors.Unwrap(uw)
|
|
}
|
|
}
|
|
|
|
|
|
func (s *state) writeCause(err error, level int ) {
|
|
if s.fDone {
|
|
s.fDone = false
|
|
s.f.sections.fields.doAfter(s)
|
|
}
|
|
|
|
nx := err
|
|
dn := false
|
|
for nx != nil {
|
|
|
|
if cs, is := err.(interface{ Cause() error }); is {
|
|
if ce := cs.Cause(); ce != nil {
|
|
if s.curSec == 1 {
|
|
s.curSec = 2
|
|
s.f.sections.cause.doBefore(s)
|
|
s.setAnsi(s.f.sections.cause.ansi)
|
|
dn = true
|
|
}
|
|
if fe := s.writeError(ce, false, true, false, level+1); fe != nil {
|
|
// llamamos al fallback del formateador
|
|
s.AddText(s.f.fbOnError(ce))
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
var lc []error
|
|
if cs, is := err.(interface{ Causes() []error }); is {
|
|
lc = cs.Causes()
|
|
} else if me, ok := err.(interface{ Unwrap() []error }); ok {
|
|
lc = me.Unwrap()
|
|
}
|
|
if len(lc) > 0 {
|
|
if s.curSec == 1 {
|
|
s.curSec = 2
|
|
s.setAnsi(s.f.sections.cause.ansi)
|
|
s.f.sections.cause.doBefore(s)
|
|
dn = true
|
|
}
|
|
for _, e := range lc {
|
|
if fe := s.writeError(e, false, false, false, level); fe != nil {
|
|
s.AddText(fmt.Sprintf(fbCauseError, reflect.TypeOf(e), fe.Error()))
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
nx = errors.Unwrap(nx)
|
|
}
|
|
|
|
if dn {
|
|
s.f.sections.cause.doAfter(s)
|
|
}
|
|
}
|
|
|
|
|
|
func (s *state) writeTrace(err error) {
|
|
if s.fDone {
|
|
s.fDone = false
|
|
s.f.sections.fields.doAfter(s)
|
|
}
|
|
|
|
tc := stack.UnwrapTrace(err,true)
|
|
if tc == nil {
|
|
return
|
|
}
|
|
|
|
s.f.sections.trace.doBefore(s)
|
|
s.setAnsi(s.f.sections.trace.ansi)
|
|
|
|
for n,f := range tc.Frames() {
|
|
s.f.frameBuilder.BuildFrame(s,f,n)
|
|
}
|
|
defer s.f.sections.trace.doAfter(s)
|
|
}
|
|
|
|
|
|
|
|
func (s *state) writeField(name, value string) {
|
|
if !s.fDone {
|
|
s.fDone = true
|
|
s.setAnsi(s.f.sections.fields.ansi)
|
|
s.f.sections.fields.doBefore(s)
|
|
}
|
|
|
|
var cv, cn ansi.Code
|
|
|
|
n := s.f.defField.NameTrans.Apply(name)
|
|
v := s.f.defField.ValueTrans.Apply(value)
|
|
|
|
if s.f.options.ShowAnsi() {
|
|
cn = s.f.defField.NameAnsi
|
|
cv = s.f.defField.ValueAnsi
|
|
}
|
|
|
|
if ff,hs := s.f.fields[name]; hs {
|
|
n = ff.NameTrans.Apply (n)
|
|
v = ff.ValueTrans.Apply(v)
|
|
if s.f.options.ShowAnsi() {
|
|
cn = ff.NameAnsi
|
|
cv = ff.ValueAnsi
|
|
}
|
|
}
|
|
|
|
s.b.WriteString(s.f.vLayouts.indent)
|
|
s.b.WriteString(cn.Sprint(n))
|
|
s.b.WriteString(cn.Sprint(s.f.vLayouts.sep))
|
|
s.b.WriteString(cv.Sprint(v))
|
|
s.b.WriteByte('\n')
|
|
|
|
}
|
|
|
|
|
|
func (s *state) writeString (str string) {
|
|
str = s.f.stringTrans.Apply(str)
|
|
s.b.WriteString(s.ansi.Sprintf(str))
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *state) setAnsi (ansi ansi.Code) {
|
|
if s.f.options.ShowAnsi() {
|
|
s.ansi = ansi
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|