blob: 99ec73678e1dd4720b511849a48872704d7c12c4 [file] [log] [blame]
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package benchstat
import (
"fmt"
"io"
"unicode/utf8"
)
// FormatText appends a fixed-width text formatting of the tables to w.
func FormatText(w io.Writer, tables []*Table) {
var textTables [][]*textRow
for _, t := range tables {
textTables = append(textTables, toText(t))
}
var max []int
for _, table := range textTables {
for _, row := range table {
if len(row.cols) == 1 {
// Header row
continue
}
for len(max) < len(row.cols) {
max = append(max, 0)
}
for i, s := range row.cols {
n := utf8.RuneCountInString(s)
if max[i] < n {
max[i] = n
}
}
}
}
for i, table := range textTables {
if i > 0 {
fmt.Fprintf(w, "\n")
}
// headings
row := table[0]
for i, s := range row.cols {
switch i {
case 0:
fmt.Fprintf(w, "%-*s", max[i], s)
default:
fmt.Fprintf(w, " %-*s", max[i], s)
case len(row.cols) - 1:
fmt.Fprintf(w, " %s\n", s)
}
}
// data
for _, row := range table[1:] {
for i, s := range row.cols {
switch {
case len(row.cols) == 1:
// Header row
fmt.Fprint(w, s)
case i == 0:
fmt.Fprintf(w, "%-*s", max[i], s)
default:
if i == len(row.cols)-1 && len(s) > 0 && s[0] == '(' {
// Left-align p value.
fmt.Fprintf(w, " %s", s)
break
}
fmt.Fprintf(w, " %*s", max[i], s)
}
}
fmt.Fprintf(w, "\n")
}
}
}
// A textRow is a row of printed text columns.
type textRow struct {
cols []string
}
func newTextRow(cols ...string) *textRow {
return &textRow{cols: cols}
}
func (r *textRow) add(col string) {
r.cols = append(r.cols, col)
}
func (r *textRow) trim() {
for len(r.cols) > 0 && r.cols[len(r.cols)-1] == "" {
r.cols = r.cols[:len(r.cols)-1]
}
}
// toText converts the Table to a textual grid of cells,
// which can then be printed in fixed-width output.
func toText(t *Table) []*textRow {
var textRows []*textRow
switch len(t.Configs) {
case 1:
textRows = append(textRows, newTextRow("name", t.Metric))
case 2:
textRows = append(textRows, newTextRow("name", "old "+t.Metric, "new "+t.Metric, "delta"))
default:
row := newTextRow("name \\ " + t.Metric)
row.cols = append(row.cols, t.Configs...)
textRows = append(textRows, row)
}
var group string
for _, row := range t.Rows {
if row.Group != group {
group = row.Group
textRows = append(textRows, newTextRow(group))
}
text := newTextRow(row.Benchmark)
for _, m := range row.Metrics {
text.cols = append(text.cols, m.Format(row.Scaler))
}
if len(t.Configs) == 2 {
delta := row.Delta
if delta == "~" {
delta = "~ "
}
text.cols = append(text.cols, delta)
text.cols = append(text.cols, row.Note)
}
textRows = append(textRows, text)
}
for _, r := range textRows {
r.trim()
}
return textRows
}