Skip to content

Commit

Permalink
feat(internal): add (*ctxerr.Error).ErrorWithoutColors method
Browse files Browse the repository at this point in the history
It is the same as (*ctxerr.Error).Error, but the string it returns
does not contain any ANSI color escape sequences.

By the way:
- util.IndentStringIn becomes util.IndentColorizeStringIn;
- add new util.IndentStringIn without colorization;
- util.IndentColorizeStringIn now also enables colorization at
  the beginning and end of string.

Signed-off-by: Maxime Soulé <btik-git@scoubidou.com>
  • Loading branch information
maxatome committed Nov 17, 2022
1 parent 0b42e3d commit a3fcff2
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 101 deletions.
66 changes: 45 additions & 21 deletions internal/ctxerr/error.go
Expand Up @@ -93,19 +93,34 @@ func TypeMismatch(got, expected reflect.Type) *Error {
func (e *Error) Error() string {
buf := strings.Builder{}

e.Append(&buf, "")
e.Append(&buf, "", true)

return buf.String()
}

// ErrorWithoutColors is the same as [Error.Error] but guarantees the
// resulting string does not contain any ANSI color escape sequences.
func (e *Error) ErrorWithoutColors() string {
buf := strings.Builder{}

e.Append(&buf, "", false)

return buf.String()
}

// Append appends the a contents to buf using prefix prefix for each
// line.
func (e *Error) Append(buf *strings.Builder, prefix string) {
func (e *Error) Append(buf *strings.Builder, prefix string, colorized bool) {
if e == BooleanError {
return
}

color.Init()
var badOn, badOff, okOn, okOff string
if colorized {
color.Init()
badOn, badOff = color.BadOn, color.BadOff
okOn, okOff = color.OKOn, color.OKOff
}

var writeEolPrefix func()
if prefix != "" {
Expand All @@ -124,13 +139,19 @@ func (e *Error) Append(buf *strings.Builder, prefix string) {
}

if e == ErrTooManyErrors {
buf.WriteString(color.TitleOn)
if colorized {
buf.WriteString(color.TitleOn)
}
buf.WriteString(e.Message)
buf.WriteString(color.TitleOff)
if colorized {
buf.WriteString(color.TitleOff)
}
return
}

buf.WriteString(color.TitleOn)
if colorized {
buf.WriteString(color.TitleOn)
}
if pos := strings.Index(e.Message, "%%"); pos >= 0 {
buf.WriteString(e.Message[:pos])
buf.WriteString(e.Context.Path.String())
Expand All @@ -140,32 +161,34 @@ func (e *Error) Append(buf *strings.Builder, prefix string) {
buf.WriteString(": ")
buf.WriteString(e.Message)
}
buf.WriteString(color.TitleOff)
if colorized {
buf.WriteString(color.TitleOff)
}

if e.Summary != nil {
buf.WriteByte('\n')
e.Summary.AppendSummary(buf, prefix+"\t")
e.Summary.AppendSummary(buf, prefix+"\t", colorized)
} else {
writeEolPrefix()
buf.WriteString(color.BadOnBold)
if colorized {
buf.WriteString(color.BadOnBold)
}
buf.WriteString("\t got: ")
buf.WriteString(color.BadOn)
util.IndentStringIn(buf, e.GotString(), prefix+"\t ", color.BadOn, color.BadOff)
buf.WriteString(color.BadOff)
util.IndentColorizeStringIn(buf, e.GotString(), prefix+"\t ", badOn, badOff)
writeEolPrefix()
buf.WriteString(color.OKOnBold)
if colorized {
buf.WriteString(color.OKOnBold)
}
buf.WriteString("\texpected: ")
buf.WriteString(color.OKOn)
util.IndentStringIn(buf, e.ExpectedString(), prefix+"\t ", color.OKOn, color.OKOff)
buf.WriteString(color.OKOff)
util.IndentColorizeStringIn(buf, e.ExpectedString(), prefix+"\t ", okOn, okOff)
}

// This error comes from another one
if e.Origin != nil {
writeEolPrefix()
buf.WriteString("Originates from following error:\n")

e.Origin.Append(buf, prefix+"\t")
e.Origin.Append(buf, prefix+"\t", colorized)
}

if e.Location.IsInitialized() &&
Expand All @@ -179,7 +202,7 @@ func (e *Error) Append(buf *strings.Builder, prefix string) {

if e.Next != nil {
buf.WriteByte('\n')
e.Next.Append(buf, prefix) // next error at same level
e.Next.Append(buf, prefix, colorized) // next error at same level
}
}

Expand All @@ -201,14 +224,15 @@ func (e *Error) ExpectedString() string {
return util.ToString(e.Expected)
}

// SummaryString returns the string corresponding to the Summary
// field. Returns the empty string if the e Summary field is nil.
// SummaryString returns the string corresponding to the Summary field
// without any ANSI color escape sequences. Returns the empty string
// if the e Summary field is nil.
func (e *Error) SummaryString() string {
if e.Summary == nil {
return ""
}

var buf strings.Builder
e.Summary.AppendSummary(&buf, "")
e.Summary.AppendSummary(&buf, "", false)
return buf.String()
}
15 changes: 15 additions & 0 deletions internal/ctxerr/error_test.go
Expand Up @@ -20,6 +20,13 @@ import (
func TestError(t *testing.T) {
defer color.SaveState()()

checkWithoutColors := func(err *ctxerr.Error) {
t.Helper()
test.EqualStr(t, err.ErrorWithoutColors(), err.Error())
defer color.SaveState(true)()
test.IsTrue(t, err.ErrorWithoutColors() != err.Error())
}

err := ctxerr.Error{
Context: ctxerr.Context{
Path: ctxerr.NewPath("DATA").AddArrayIndex(12).AddField("Field"),
Expand All @@ -32,6 +39,7 @@ func TestError(t *testing.T) {
`DATA[12].Field: error message
got: 1
expected: 2`)
checkWithoutColors(&err)
test.EqualStr(t, err.GotString(), "1")
test.EqualStr(t, err.ExpectedString(), "2")
test.EqualStr(t, err.SummaryString(), "")
Expand All @@ -41,18 +49,21 @@ func TestError(t *testing.T) {
`Value of DATA[12].Field differ
got: 1
expected: 2`)
checkWithoutColors(&err)

err.Message = "Path at end: %%"
test.EqualStr(t, err.Error(),
`Path at end: DATA[12].Field
got: 1
expected: 2`)
checkWithoutColors(&err)

err.Message = "%% <- the path!"
test.EqualStr(t, err.Error(),
`DATA[12].Field <- the path!
got: 1
expected: 2`)
checkWithoutColors(&err)

err = ctxerr.Error{
Context: ctxerr.Context{
Expand All @@ -72,6 +83,7 @@ func TestError(t *testing.T) {
got: 1
expected: 2
[under operator Operator at file.go:23]`)
checkWithoutColors(&err)

err = ctxerr.Error{
Context: ctxerr.Context{
Expand Down Expand Up @@ -105,6 +117,7 @@ Originates from following error:
42
[under operator SubOperator at file2.go:236]
[under operator Operator at file.go:23]`)
checkWithoutColors(&err)
test.EqualStr(t, err.GotString(), "")
test.EqualStr(t, err.ExpectedString(), "")
test.EqualStr(t, err.SummaryString(), "666")
Expand Down Expand Up @@ -156,6 +169,7 @@ Originates from following error:
DATA[13].Field: error message
888
[under operator Operator at file.go:23]`)
checkWithoutColors(&err)

err = ctxerr.Error{
Context: ctxerr.Context{Path: ctxerr.NewPath("DATA").AddArrayIndex(12).AddField("Field")},
Expand Down Expand Up @@ -203,6 +217,7 @@ Originates from following error:
DATA[13].Field: error message
888
[under operator Operator at file.go:24]`)
checkWithoutColors(&err)

//
// ErrTooManyErrors
Expand Down
39 changes: 20 additions & 19 deletions internal/ctxerr/summary.go
Expand Up @@ -16,7 +16,7 @@ import (
// ErrorSummary is the interface used to render error summaries. See
// Error.Summary.
type ErrorSummary interface {
AppendSummary(buf *strings.Builder, prefix string)
AppendSummary(buf *strings.Builder, prefix string, colorized bool)
}

// ErrorSummaryItem implements the [ErrorSummary] interface and allows
Expand All @@ -39,26 +39,25 @@ type ErrorSummaryItem struct {
var _ ErrorSummary = ErrorSummaryItem{}

// AppendSummary implements the [ErrorSummary] interface.
func (s ErrorSummaryItem) AppendSummary(buf *strings.Builder, prefix string) {
color.Init()

func (s ErrorSummaryItem) AppendSummary(buf *strings.Builder, prefix string, colorized bool) {
buf.WriteString(prefix)
buf.WriteString(color.BadOnBold)

badOn, badOff := "", ""
if colorized {
color.Init()
badOn, badOff = color.BadOn, color.BadOff
buf.WriteString(color.BadOnBold)
}
buf.WriteString(s.Label)
buf.WriteString(": ")

buf.WriteString(color.BadOn)
util.IndentStringIn(buf, s.Value, prefix+strings.Repeat(" ", len(s.Label)+2), color.BadOn, color.BadOff)
util.IndentColorizeStringIn(buf, s.Value, prefix+strings.Repeat(" ", len(s.Label)+2), badOn, badOff)

if s.Explanation != "" {
buf.WriteString(color.BadOff)
buf.WriteByte('\n')
buf.WriteString(prefix)
buf.WriteString(color.BadOn)
util.IndentStringIn(buf, s.Explanation, prefix, color.BadOn, color.BadOff)
util.IndentColorizeStringIn(buf, s.Explanation, prefix, badOn, badOff)
}

buf.WriteString(color.BadOff)
}

// ErrorSummaryItems implements the [ErrorSummary] interface and
Expand All @@ -71,7 +70,7 @@ type ErrorSummaryItems []ErrorSummaryItem
var _ ErrorSummary = (ErrorSummaryItems)(nil)

// AppendSummary implements [ErrorSummary] interface.
func (s ErrorSummaryItems) AppendSummary(buf *strings.Builder, prefix string) {
func (s ErrorSummaryItems) AppendSummary(buf *strings.Builder, prefix string, colorized bool) {
maxLen := 0
for _, item := range s {
if len(item.Label) > maxLen {
Expand All @@ -86,21 +85,23 @@ func (s ErrorSummaryItems) AppendSummary(buf *strings.Builder, prefix string) {
if len(item.Label) < maxLen {
item.Label = strings.Repeat(" ", maxLen-len(item.Label)) + item.Label
}
item.AppendSummary(buf, prefix)
item.AppendSummary(buf, prefix, colorized)
}
}

type errorSummaryString string

var _ ErrorSummary = errorSummaryString("")

func (s errorSummaryString) AppendSummary(buf *strings.Builder, prefix string) {
color.Init()
func (s errorSummaryString) AppendSummary(buf *strings.Builder, prefix string, colorized bool) {
badOn, badOff := "", ""
if colorized {
color.Init()
badOn, badOff = color.BadOn, color.BadOff
}

buf.WriteString(prefix)
buf.WriteString(color.BadOn)
util.IndentStringIn(buf, string(s), prefix, color.BadOn, color.BadOff)
buf.WriteString(color.BadOff)
util.IndentColorizeStringIn(buf, string(s), prefix, badOn, badOff)
}

// NewSummary returns an ErrorSummary composed by the simple string s.
Expand Down

0 comments on commit a3fcff2

Please sign in to comment.