// ------------------------------------------------------------------------ // Project active2 // Active Thing (activething.com) git.activething.com/go // // File name text_builder.go // Created by DEV // Modified 22/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" "bins/mems" "errs/collects" "fmt" "strings" ) type ( TextBuilder struct { f *TextFormatter b *mems.ABuffer useAux bool auxB *mems.ABuffer level int ansiC ansi.Code invalid error } TextBuilderFnc func (b *TextBuilder) ) func newTextBuilder(formatter *TextFormatter) *TextBuilder { b := &TextBuilder{ f : formatter, b : mems.NewABuffer(), useAux: len(formatter.valueTrans) > 0, ansiC : formatter.layout.AnsiC, } if b.useAux { b.auxB = mems.NewABuffer() } else { b.auxB = b.b } return b } func (b *TextBuilder) Level () int { return b.level } func (b *TextBuilder) AddText (text string) { b.b.WriteString(text) } func (b *TextBuilder) AddTextAnsi (ansiC ansi.Code, text string) { if b.f.HasShowAnsi() { if ansiC == 0 { ansiC = b.ansiC } b.b.WriteString(ansiC.Begin()) defer b.b.WriteString(ansiC.End()) } b.b.WriteString(text) } func (b *TextBuilder) SetAnsi(ansiC ansi.Code) { if ansiC != 0 { b.ansiC = ansiC } else { b.ansiC = b.f.layout.AnsiC } } func (b *TextBuilder) format (err error) { c := collects.NewErrorCollection(err) if c.Collect(b.f.HasAddFields(), b.f.HasAddCauses(), b.f.HasAddTrace()); c.Invalid != nil { //b.writeFormatError(cl.Invalid) return } b.write(c, 0) } func (b *TextBuilder) write (c *collects.ErrorCollection, level int) { if b.invalid != nil { return } ind := strings.Repeat(b.f.layout.Indent,level) d := false f := b.f b.level = level if level == 0 { b.f.sections.Main.begin(b) } defer func(bl *TextBuilder, dn *bool,er error){ if !(*dn) { if rc := recover(); rc != nil { if er,is := rc.(error); is { bl.invalid = er } else { bl.invalid = fmt.Errorf("%#v",rc) } bl.f.onFormatError(bl,er,bl.invalid) } } }(b,&d, c.Err) f.sections.Head.begin(b) b.writeFieldNameValue(collects.FieldNameError, c.ErrStr, ind) if f.HasAddTypes() { if len(c.TypesStr) == 1 { b.writeFieldNameValue(collects.FieldNameType, c.TypesStr[0], ind) } else { b.writeFieldNameValue(collects.FieldNameTypes, strings.Join(c.TypesStr, ","), ind) } } f.sections.Head.end(b) if f.HasAddFields() && c.Fields.Len() > 0 { f.sections.Fields.begin(b) heads := c.Fields.GetHeads() for _,fl := range heads { b.writeField(c.Fields,fl,ind) } f.sections.Fields.end(b) } if len(c.Causes) > 0 { if level == 0 { f.sections.Causes.begin(b) } b.level++ for _,cs := range c.Causes { f.sections.Cause.begin(b) b.write(&cs,level+1) f.sections.Cause.end(b) } if level == 0 { f.sections.Causes.end(b) } } if c.Trace != nil { f.sections.Trace.begin(b) for nu,fm := range c.Trace.Frames() { b.f.frameBuilder(b,fm,nu) } f.sections.Trace.end(b) } if level == 0 { b.f.sections.Main.end(b) } d = true } func (b *TextBuilder) reset () { b.b.Reset() if b.useAux { b.auxB.Reset() b.useAux = false } b.f = nil b.level = 0 b.invalid = nil } func (b *TextBuilder) writeFieldBegin (name, indent string) { if b.ansiC != 0 { b.b.WriteString(b.ansiC.Begin()) } b.b.WriteString(indent) b.b.WriteString(b.f.nameTrans.Transform(name)) b.b.WriteString(b.f.layout.FieldSep) } func (b *TextBuilder) writeFieldEnd () { b.b.WriteByte('\n') if b.ansiC != 0 { b.b.WriteString(b.ansiC.End()) } } func (b *TextBuilder) writeFieldNameValue(name string, value string, indent string) { p:= b.b.Len() b.writeFieldBegin(name,indent) if b.f.HasIgnoreEmpty() && len(value) == 0 { b.b.Truncate(p) return } b.b.WriteString(b.f.valueTrans.Transform(value)) b.writeFieldEnd() } func (b *TextBuilder) writeField (fields collects.Fields, field *collects.FieldHead, indent string) { p := b.b.Len() b.writeFieldBegin(field.Name,indent) if !b.writeFieldValue(fields,field) { b.b.Truncate(p) return } b.writeFieldEnd() } func (b *TextBuilder) writeFieldValue (fields collects.Fields, field *collects.FieldHead) bool { switch field.Kind { case collects.FieldKindFloat: b.auxB.WriteFloatStr(fields.GetFieldValueFloat(field),'f',-1) case collects.FieldKindInt: b.auxB.WriteIntStr(int64(fields.GetFieldValueInt(field)),10) case collects.FieldKindString: s := fields.GetFieldValueString(field) if b.f.HasIgnoreEmpty() && len(s) == 0 { return false } b.auxB.WriteString(fields.GetFieldValueString(field)) case collects.FieldKindTime: b.auxB.WriteDateStr(fields.GetFieldValueDate(field), b.f.layout.TimeLayout) case collects.FieldKindDate: b.auxB.WriteDateStr(fields.GetFieldValueDate(field), b.f.layout.DateLayout) case collects.FieldKindBool: b.auxB.WriteBoolStr(fields.GetFieldValueBool(field)) default: a := fields.GetFieldValueAny(field) if a == nil { if b.f.HasIgnoreEmpty() { return false } b.auxB.WOQ(false,'"', g.nullValue) } else { s := fmt.Sprintf("%#v",a) if b.f.HasIgnoreEmpty() && len(s) == 0 { return false } b.auxB.WriteString(s) } } if b.useAux { b.b.WriteString(b.f.valueTrans.Transform(string(b.auxB.Bytes()))) b.auxB.Reset() } return true }