| // 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 ( | |
| "bytes" | |
| "strings" | |
| "github.com/google/safehtml" | |
| "github.com/google/safehtml/template" | |
| ) | |
| var htmlTemplate = template.Must(template.New("").Funcs(htmlFuncs).Parse(` | |
| {{- if . -}} | |
| {{with index . 0}} | |
| <table class='benchstat {{if .OldNewDelta}}oldnew{{end}}'> | |
| {{if eq (len .Configs) 1}} | |
| {{- else -}} | |
| <tr class='configs'><th>{{range .Configs}}<th>{{.}}{{end}} | |
| {{end}} | |
| {{end}} | |
| {{- range $i, $table := .}} | |
| <tbody> | |
| {{if eq (len .Configs) 1}} | |
| <tr><th><th>{{.Metric}} | |
| {{else -}} | |
| <tr><th><th colspan='{{len .Configs}}' class='metric'>{{.Metric}}{{if .OldNewDelta}}<th>delta{{end}} | |
| {{end}}{{range $group := group $table.Rows -}} | |
| {{if and (gt (len $table.Groups) 1) (len (index . 0).Group)}}<tr class='group'><th colspan='{{colspan (len $table.Configs) $table.OldNewDelta}}'>{{(index . 0).Group}}{{end}} | |
| {{- range $row := . -}} | |
| {{if $table.OldNewDelta -}} | |
| <tr class='{{if eq .Change 1}}better{{else if eq .Change -1}}worse{{else}}unchanged{{end}}'> | |
| {{- else -}} | |
| <tr> | |
| {{- end -}} | |
| <td>{{.Benchmark}}{{range .Metrics}}<td>{{.Format $row.Scaler}}{{end}}{{if $table.OldNewDelta}}<td class='{{if eq .Delta "~"}}nodelta{{else}}delta{{end}}'>{{replace .Delta "-" "−" -1}}<td class='note'>{{.Note}}{{end}} | |
| {{end -}} | |
| {{- end -}} | |
| <tr><td> | |
| </tbody> | |
| {{end}} | |
| </table> | |
| {{end -}} | |
| `)) | |
| var htmlFuncs = template.FuncMap{ | |
| "replace": strings.Replace, | |
| "group": htmlGroup, | |
| "colspan": htmlColspan, | |
| } | |
| func htmlColspan(configs int, delta bool) int { | |
| if delta { | |
| configs++ | |
| } | |
| return configs + 1 | |
| } | |
| func htmlGroup(rows []*Row) (out [][]*Row) { | |
| var group string | |
| var cur []*Row | |
| for _, r := range rows { | |
| if r.Group != group { | |
| group = r.Group | |
| if len(cur) > 0 { | |
| out = append(out, cur) | |
| cur = nil | |
| } | |
| } | |
| cur = append(cur, r) | |
| } | |
| if len(cur) > 0 { | |
| out = append(out, cur) | |
| } | |
| return | |
| } | |
| // SafeFormatHTML returns the HTML formatting of the tables. | |
| func SafeFormatHTML(tables []*Table) safehtml.HTML { | |
| h, err := htmlTemplate.ExecuteToHTML(tables) | |
| if err != nil { | |
| // Only possible errors here are template not matching data structure. | |
| // Don't make caller check - it's our fault. | |
| panic(err) | |
| } | |
| return h | |
| } | |
| // FormatHTML appends an HTML formatting of the tables to buf. | |
| func FormatHTML(buf *bytes.Buffer, tables []*Table) { | |
| err := htmlTemplate.Execute(buf, tables) | |
| if err != nil { | |
| // Only possible errors here are template not matching data structure. | |
| // Don't make caller check - it's our fault. | |
| panic(err) | |
| } | |
| } |