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.
429 lines
7.6 KiB
429 lines
7.6 KiB
// ------------------------------------------------------------------------
|
|
// Project active2
|
|
// Active Thing (activething.com) git.activething.com/go
|
|
//
|
|
// File name formatter_stae.go
|
|
// Created by DEV
|
|
// Modified 16/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
|
|
|
|
import (
|
|
"ansi"
|
|
"bytes"
|
|
"errors"
|
|
"errs/fmtr"
|
|
"errs/stack"
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type (
|
|
|
|
TextBuilder struct {
|
|
f *TextFormatter
|
|
b bytes.Buffer
|
|
|
|
numFields int
|
|
indent string
|
|
level int
|
|
ansiC ansi.Code
|
|
unwrap bool
|
|
}
|
|
|
|
|
|
TextBuilderFnc func (s *TextBuilder)
|
|
)
|
|
|
|
|
|
func newTextBuilder(formatter *TextFormatter) *TextBuilder {
|
|
return &TextBuilder{
|
|
f : formatter,
|
|
ansiC: formatter.layout.AnsiC}}
|
|
|
|
|
|
|
|
func (s *TextBuilder) Indent() string {
|
|
return s.indent }
|
|
|
|
func (s *TextBuilder) Level () int {
|
|
return s.level }
|
|
|
|
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(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)
|
|
}
|
|
|
|
|
|
|
|
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.layout.IntLayout,value),false)}
|
|
|
|
func (s *TextBuilder) AddFieldBool(name string, value bool ) {
|
|
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,false) }
|
|
|
|
func (s *TextBuilder) AddFieldFloat(name string, value float64) {
|
|
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.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.layout.DateLayout),false) }
|
|
|
|
|
|
|
|
|
|
func (s *TextBuilder) Build(err error, fbuilder fmtr.BuildFieldFnc) {
|
|
if err == nil {
|
|
return
|
|
}
|
|
|
|
defer func(bl *TextBuilder){
|
|
if re:=recover(); re != nil {
|
|
er,is := re.(error)
|
|
if !is {
|
|
er = fmt.Errorf("%#v",re)
|
|
}
|
|
s.f.onFormatError(bl,err,er)
|
|
}
|
|
}(s)
|
|
|
|
if !s.unwrap {
|
|
if s.level == 0 {
|
|
s.f.sections.Head.begin(s)
|
|
}
|
|
s.writeField(fmtr.FieldNameError,err.Error(),true)
|
|
if s.f.AddType() {
|
|
s.writeField(fmtr.FieldNameType, reflect.TypeOf(err).String(),true)
|
|
}
|
|
if s.level == 0 {
|
|
s.f.sections.Head.end(s)
|
|
}
|
|
}
|
|
|
|
if s.f.AddCode() {
|
|
if ec,is := err.(interface { Code() string}); is {
|
|
cd := ec.Code()
|
|
if cd != err.Error() {
|
|
s.writeField(fmtr.FieldNameCode, cd, false)
|
|
}
|
|
}
|
|
}
|
|
|
|
if fbuilder != nil {
|
|
if s.level == 0 {
|
|
s.f.sections.Fields.begin(s)
|
|
}
|
|
fbuilder(s)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *TextBuilder) format (err error) {
|
|
|
|
s.f.sections.Main.begin(s)
|
|
d := false
|
|
defer func(bl *TextBuilder, dn *bool){
|
|
if !(*dn) {
|
|
if rc:= recover(); rc != nil {
|
|
er,is := rc.(error)
|
|
if !is {
|
|
er = fmt.Errorf("%#v",rc)
|
|
}
|
|
bl.f.onFormatError(bl,err,er)
|
|
}
|
|
}
|
|
s.f.sections.Main.end(bl)
|
|
}(s,&d)
|
|
|
|
|
|
var fe error
|
|
if fe = s.addError(err); fe != nil {
|
|
s.f.onFormatError(s,err,fe)
|
|
return
|
|
}
|
|
|
|
if s.numFields > 0 {
|
|
s.f.sections.Fields.end(s)
|
|
}
|
|
|
|
if s.f.AddCause() {
|
|
cl := getCauses(err)
|
|
if len(cl) > 0 {
|
|
s.f.sections.Causes.begin(s)
|
|
if fe = s.addCauses(cl); fe != nil {
|
|
s.f.onCauseError(s, err, fe)
|
|
}
|
|
s.f.sections.Causes.end(s)
|
|
}
|
|
}
|
|
|
|
if s.f.AddTrace() {
|
|
if fe = s.addTrace(err); fe != nil {
|
|
s.f.onTraceError(s,err,fe)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *TextBuilder) addError(err error) (fe error) {
|
|
if err == nil {
|
|
return
|
|
}
|
|
|
|
dn := true
|
|
defer func (d *bool, e *error){
|
|
if !(*d) {
|
|
if re := recover(); re != nil {
|
|
if er,is := re.(error); is {
|
|
*e = er
|
|
} else {
|
|
*e = fmt.Errorf("%#v",re)
|
|
}
|
|
}
|
|
}
|
|
}(&dn,&fe)
|
|
|
|
|
|
if eb,is := err.(fmtr.Formattable); is {
|
|
eb.BuildFormat(s)
|
|
} else {
|
|
rt := reflect.TypeOf(err)
|
|
if rt.Kind() == reflect.Pointer {
|
|
rt = rt.Elem()
|
|
}
|
|
eb := fmtr.GetErrorTypeBuilder(rt)
|
|
if eb != nil {
|
|
eb.BuildError(s,err)
|
|
} else {
|
|
eb = fmtr.MakeErrorTypeBuilder(s, err)
|
|
fmtr.SetErrorTypeBuilder(rt, eb)
|
|
}
|
|
}
|
|
|
|
|
|
if s.f.AddFields() {
|
|
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, ne, fe)
|
|
return fe
|
|
}
|
|
}
|
|
}
|
|
|
|
dn = true
|
|
|
|
return fe
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *TextBuilder) addCauses (causes []error) (fe error) {
|
|
if len(causes) == 0 {
|
|
return
|
|
}
|
|
for _,ce := range causes {
|
|
s.incLevel()
|
|
s.f.sections.Cause.begin(s)
|
|
|
|
if fe := s.addError(ce); fe == nil {
|
|
fe = s.addCauses(getCauses(ce))
|
|
}
|
|
if fe != nil {
|
|
s.f.onCauseError(s,ce,fe)
|
|
}
|
|
|
|
s.f.sections.Cause.end(s)
|
|
s.decLevel()
|
|
|
|
if fe != nil {
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
|
|
func getCauses (err error) []error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
ne := err
|
|
for ne != nil {
|
|
if cs, is := ne.(interface{ Cause() error }); is {
|
|
ce := cs.Cause()
|
|
if ce == nil {
|
|
return nil
|
|
}
|
|
return []error{ce}
|
|
}
|
|
if cs, is := ne.(interface{ Causes() []error }); is {
|
|
return cs.Causes()
|
|
}
|
|
if cs, is := ne.(interface{ Unwrap() []error }); is {
|
|
return cs.Unwrap()
|
|
}
|
|
ne = errors.Unwrap(ne)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
|
|
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
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *TextBuilder) setAnsi(ansiC ansi.Code) {
|
|
if ansiC != 0 {
|
|
s.ansiC = ansiC
|
|
} else {
|
|
s.ansiC = s.f.layout.AnsiC
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (s *TextBuilder) writeField (name string, value string, head bool) {
|
|
if !head {
|
|
if !s.f.AddFields() {
|
|
return
|
|
}
|
|
s.numFields++
|
|
}
|
|
|
|
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.layout.FieldSep)
|
|
s.b.WriteString(v)
|
|
s.b.WriteByte('\n')
|
|
|
|
if s.ansiC != 0 {
|
|
s.b.WriteString(s.ansiC.String())
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *TextBuilder) incLevel () {
|
|
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.UseIndent() && s.level > 0 {
|
|
s.indent = strings.Repeat(s.f.layout.Indent,s.level)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|