xerrors: gobble colon and newline in edge cases
We gobble newlines at the start of a detailed
section, at end before a new message, but not
at the end of a message. This CL add that.
Also, we should not print the colon after the last
non-detailed message if the detailed message
consists solely of newlines that are gobbled.
Change-Id: I9888253e600fa1d7a05dd61f2cc49cda4275ceb1
Reviewed-on: https://go-review.googlesource.com/c/160717
Reviewed-by: Damien Neil <dneil@google.com>
Run-TryBot: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/adaptor.go b/adaptor.go
index 83a7945..4317f24 100644
--- a/adaptor.go
+++ b/adaptor.go
@@ -70,8 +70,6 @@
loop:
for {
- p.inDetail = false
-
switch v := err.(type) {
case Formatter:
err = v.FormatError((*printer)(p))
@@ -85,15 +83,13 @@
if err == nil {
break
}
- if !p.inDetail || !p.printDetail {
+ if p.needColon || !p.printDetail {
p.buf.WriteByte(':')
- }
- // Strip last newline of detail.
- if bytes.HasSuffix(p.buf.Bytes(), detailSep) {
- p.buf.Truncate(p.buf.Len() - len(detailSep))
+ p.needColon = false
}
p.buf.WriteString(sep)
p.inDetail = false
+ p.needNewline = false
}
exit:
@@ -135,6 +131,7 @@
printDetail bool
inDetail bool
+ needColon bool
needNewline bool
}
@@ -143,24 +140,32 @@
if len(b) == 0 {
return 0, nil
}
- if s.inDetail && s.needNewline {
- s.needNewline = false
- s.buf.WriteByte(':')
- s.buf.Write(detailSep)
+ if s.inDetail && s.needColon {
+ s.needNewline = true
if b[0] == '\n' {
b = b[1:]
}
}
k := 0
for i, c := range b {
+ if s.needNewline {
+ if s.inDetail && s.needColon {
+ s.buf.WriteByte(':')
+ s.needColon = false
+ }
+ s.buf.Write(detailSep)
+ s.needNewline = false
+ }
if c == '\n' {
s.buf.Write(b[k:i])
- s.buf.Write(detailSep)
k = i + 1
+ s.needNewline = true
}
}
s.buf.Write(b[k:])
- s.needNewline = !s.inDetail
+ if !s.inDetail {
+ s.needColon = true
+ }
} else if !s.inDetail {
s.buf.Write(b)
}
diff --git a/fmt_test.go b/fmt_test.go
index 8534c29..6744b8a 100644
--- a/fmt_test.go
+++ b/fmt_test.go
@@ -132,6 +132,26 @@
"\n and the 12 monkeys" +
"\n are laughing",
}, {
+ err: &oneNewline{nil},
+ fmt: "%+v",
+ want: "123",
+ }, {
+ err: &oneNewline{&oneNewline{nil}},
+ fmt: "%+v",
+ want: "123:" +
+ "\n - 123",
+ }, {
+ err: &newlineAtEnd{nil},
+ fmt: "%+v",
+ want: "newlineAtEnd:\n detail",
+ }, {
+ err: &newlineAtEnd{&newlineAtEnd{nil}},
+ fmt: "%+v",
+ want: "newlineAtEnd:" +
+ "\n detail" +
+ "\n - newlineAtEnd:" +
+ "\n detail",
+ }, {
err: framed,
fmt: "%+v",
want: "something:" +
@@ -302,7 +322,7 @@
var ok bool
if tc.regexp {
var err error
- ok, err = regexp.MatchString(tc.want, got)
+ ok, err = regexp.MatchString(tc.want+"$", got)
if err != nil {
t.Fatal(err)
}
@@ -420,6 +440,42 @@
return nil
}
+type oneNewline struct {
+ next error
+}
+
+func (e *oneNewline) Error() string { return fmt.Sprint(e) }
+
+func (e *oneNewline) Format(s fmt.State, verb rune) {
+ xerrors.FormatError(e, s, verb)
+}
+
+func (e *oneNewline) FormatError(p xerrors.Printer) (next error) {
+ p.Print("1")
+ p.Print("2")
+ p.Print("3")
+ p.Detail()
+ p.Print("\n")
+ return e.next
+}
+
+type newlineAtEnd struct {
+ next error
+}
+
+func (e *newlineAtEnd) Error() string { return fmt.Sprint(e) }
+
+func (e *newlineAtEnd) Format(s fmt.State, verb rune) {
+ xerrors.FormatError(e, s, verb)
+}
+
+func (e *newlineAtEnd) FormatError(p xerrors.Printer) (next error) {
+ p.Print("newlineAtEnd")
+ p.Detail()
+ p.Print("detail\n")
+ return e.next
+}
+
type adapted struct {
msg string
err error