summaryrefslogtreecommitdiff
path: root/internal/goprecords/report.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/goprecords/report.go')
-rw-r--r--internal/goprecords/report.go285
1 files changed, 0 insertions, 285 deletions
diff --git a/internal/goprecords/report.go b/internal/goprecords/report.go
index cd86fcd..7d04ce3 100644
--- a/internal/goprecords/report.go
+++ b/internal/goprecords/report.go
@@ -1,158 +1,11 @@
package goprecords
import (
- "flag"
"fmt"
- "html/template"
"io"
- "net/url"
"sort"
- "strconv"
- "strings"
)
-// ReportConfig holds parsed report configuration.
-type ReportConfig struct {
- Category Category
- Metric Metric
- Limit uint
- OutputFormat OutputFormat
- All bool
- IncludeKernel bool
- StatsOrder string
-}
-
-// ReportFlags holds flag pointers registered on a FlagSet.
-type ReportFlags struct {
- category *string
- metric *string
- limit *uint
- outputFormat *string
- all *bool
- includeKernel *bool
- statsOrder *string
-}
-
-// RegisterReportFlags registers common report flags on the given FlagSet.
-func RegisterReportFlags(fs *flag.FlagSet) *ReportFlags {
- return &ReportFlags{
- category: fs.String("category", "Host", "Category: Host, Kernel, KernelMajor, KernelName"),
- metric: fs.String("metric", "Uptime", "Metric: Boots, Uptime, Score, Downtime, Lifespan"),
- limit: fs.Uint("limit", 20, "Limit output to num of entries"),
- outputFormat: fs.String("output-format", "Plaintext", "Output format: Plaintext, Markdown, Gemtext, HTML"),
- all: fs.Bool("all", false, "Generate all possible stats but Kernel"),
- includeKernel: fs.Bool("include-kernel", false, "Also include Kernel when using -all"),
- statsOrder: fs.String("stats-order", "", "Comma-separated Category:Metric order for -all"),
- }
-}
-
-// Parse converts flag values into a ReportConfig.
-func (rf *ReportFlags) Parse() (ReportConfig, error) {
- cat, err := ParseCategory(*rf.category)
- if err != nil {
- return ReportConfig{}, err
- }
- met, err := ParseMetric(*rf.metric)
- if err != nil {
- return ReportConfig{}, err
- }
- outFmt, err := ParseOutputFormat(*rf.outputFormat)
- if err != nil {
- return ReportConfig{}, err
- }
- return ReportConfig{
- Category: cat,
- Metric: met,
- Limit: *rf.limit,
- OutputFormat: outFmt,
- All: *rf.all,
- IncludeKernel: *rf.includeKernel,
- StatsOrder: *rf.statsOrder,
- }, nil
-}
-
-// ParseReportQuery builds a ReportConfig from URL query parameters using the
-// same names and defaults as RegisterReportFlags (category, metric, limit,
-// output-format, all, include-kernel, stats-order). It also accepts Category,
-// Metric, and OutputFormat as alternate keys (same values as the CLI).
-func ParseReportQuery(q url.Values) (ReportConfig, error) {
- catStr := firstQuery(q, "category", "Category")
- if catStr == "" {
- catStr = "Host"
- }
- cat, err := ParseCategory(catStr)
- if err != nil {
- return ReportConfig{}, err
- }
- metStr := firstQuery(q, "metric", "Metric")
- if metStr == "" {
- metStr = "Uptime"
- }
- met, err := ParseMetric(metStr)
- if err != nil {
- return ReportConfig{}, err
- }
- limit := uint(20)
- if ls := q.Get("limit"); ls != "" {
- v, err := strconv.ParseUint(ls, 10, 32)
- if err != nil {
- return ReportConfig{}, fmt.Errorf("invalid limit %q", ls)
- }
- limit = uint(v)
- }
- outStr := firstQuery(q, "output-format", "OutputFormat")
- if outStr == "" {
- outStr = "Plaintext"
- }
- outFmt, err := ParseOutputFormat(outStr)
- if err != nil {
- return ReportConfig{}, err
- }
- all := false
- if v := q.Get("all"); v != "" {
- all, err = parseQueryBool(v)
- if err != nil {
- return ReportConfig{}, err
- }
- }
- includeKernel := false
- if v := q.Get("include-kernel"); v != "" {
- includeKernel, err = parseQueryBool(v)
- if err != nil {
- return ReportConfig{}, err
- }
- }
- return ReportConfig{
- Category: cat,
- Metric: met,
- Limit: limit,
- OutputFormat: outFmt,
- All: all,
- IncludeKernel: includeKernel,
- StatsOrder: q.Get("stats-order"),
- }, nil
-}
-
-func firstQuery(q url.Values, keys ...string) string {
- for _, k := range keys {
- if v := q.Get(k); v != "" {
- return v
- }
- }
- return ""
-}
-
-func parseQueryBool(s string) (bool, error) {
- switch strings.ToLower(strings.TrimSpace(s)) {
- case "true", "1", "yes":
- return true, nil
- case "false", "0", "no", "":
- return false, nil
- default:
- return false, fmt.Errorf("invalid boolean %q", s)
- }
-}
-
// WriteReports renders reports to w based on the given config.
func WriteReports(w io.Writer, aggregates *Aggregates, cfg ReportConfig) error {
if !cfg.All {
@@ -395,141 +248,3 @@ func (r reportBuilder) humanStrAgg(a *Aggregate) string {
return formatDuration(a.Uptime)
}
}
-
-func (r reportBuilder) formatReport(rows []tableRow, hasLastKernel bool, outputFormat OutputFormat) string {
- cW, nW, vW, lkW := r.reportWidths(rows, hasLastKernel)
- border := r.buildBorder(cW, nW, vW, lkW, hasLastKernel)
- header := r.buildReportHeader(cW, nW, vW, lkW, hasLastKernel, border, outputFormat)
- fmtStr := r.buildFormatStr(cW, nW, vW, lkW, hasLastKernel)
- body := r.buildReportBody(rows, fmtStr, hasLastKernel)
- out := header + body + border
- if outputFormat == FormatMarkdown || outputFormat == FormatGemtext {
- out += "```\n"
- }
- return out
-}
-
-func (r reportBuilder) formatReportHTML(rows []tableRow, hasLastKernel bool) string {
- cW, nW, vW, lkW := r.reportWidths(rows, hasLastKernel)
- border := r.buildBorder(cW, nW, vW, lkW, hasLastKernel)
- fmtStr := r.buildFormatStr(cW, nW, vW, lkW, hasLastKernel)
- var headRow string
- if hasLastKernel {
- headRow = fmt.Sprintf(fmtStr+"\n", "Pos", r.category.String(), r.metric.String(), "Last Kernel")
- } else {
- headRow = fmt.Sprintf(fmtStr+"\n", "Pos", r.category.String(), r.metric.String())
- }
- body := r.buildReportBody(rows, fmtStr, hasLastKernel)
- ascii := border + headRow + border + body + border
-
- hl := int(r.headerIndent)
- if hl < 1 {
- hl = 1
- }
- if hl > 6 {
- hl = 6
- }
- title := fmt.Sprintf("Top %d %s's by %s", r.limit, r.metric, r.category)
- desc := MetricDescription(r.metric)
-
- var b strings.Builder
- b.WriteString("<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>")
- b.WriteString(template.HTMLEscapeString(title))
- b.WriteString("</title>\n</head>\n<body>\n<h")
- b.WriteString(strconv.Itoa(hl))
- b.WriteString(">")
- b.WriteString(template.HTMLEscapeString(title))
- b.WriteString("</h")
- b.WriteString(strconv.Itoa(hl))
- b.WriteString(">\n")
- if desc != "" {
- b.WriteString("<p>")
- b.WriteString(template.HTMLEscapeString(desc))
- b.WriteString("</p>\n")
- }
- b.WriteString("<pre>")
- b.WriteString(template.HTMLEscapeString(ascii))
- b.WriteString("</pre>\n</body>\n</html>\n")
- return b.String()
-}
-
-func (r reportBuilder) reportWidths(rows []tableRow, hasLastKernel bool) (countW, nameW, valueW, lastKernelW int) {
- countW = 3
- nameW = len(r.category.String())
- valueW = len(r.metric.String())
- if hasLastKernel {
- lastKernelW = len("Last Kernel")
- }
- for _, row := range rows {
- if len(row.Pos) > countW {
- countW = len(row.Pos)
- }
- if len(row.Name) > nameW {
- nameW = len(row.Name)
- }
- if len(row.Value) > valueW {
- valueW = len(row.Value)
- }
- if len(row.LastKernel) > lastKernelW {
- lastKernelW = len(row.LastKernel)
- }
- }
- return countW, nameW, valueW, lastKernelW
-}
-
-func (r reportBuilder) buildBorder(countW, nameW, valueW, lastKernelW int, hasLastKernel bool) string {
- parts := []string{
- "+" + strings.Repeat("-", 2+countW),
- "+" + strings.Repeat("-", 2+nameW),
- "+" + strings.Repeat("-", 2+valueW),
- }
- if hasLastKernel {
- parts = append(parts, "+"+strings.Repeat("-", 2+lastKernelW))
- }
- return strings.Join(parts, "") + "+\n"
-}
-
-func (r reportBuilder) buildReportHeader(countW, nameW, valueW, lastKernelW int, hasLastKernel bool, border string, outputFormat OutputFormat) string {
- var h string
- if outputFormat == FormatMarkdown || outputFormat == FormatGemtext {
- h = strings.Repeat("#", int(r.headerIndent)) + " "
- }
- h += fmt.Sprintf("Top %d %s's by %s\n\n", r.limit, r.metric, r.category)
- desc := MetricDescription(r.metric)
- lineLimit := len(border)
- if outputFormat == FormatPlaintext && lineLimit > 0 && len(desc) > lineLimit-1 {
- desc = " " + wordWrap(desc, lineLimit-1)
- }
- h += desc + "\n\n"
- if outputFormat == FormatMarkdown || outputFormat == FormatGemtext {
- h += "```\n"
- }
- h += border
- fmtStr := r.buildFormatStr(countW, nameW, valueW, lastKernelW, hasLastKernel)
- if hasLastKernel {
- h += fmt.Sprintf(fmtStr+"\n", "Pos", r.category.String(), r.metric.String(), "Last Kernel")
- } else {
- h += fmt.Sprintf(fmtStr+"\n", "Pos", r.category.String(), r.metric.String())
- }
- h += border
- return h
-}
-
-func (r reportBuilder) buildFormatStr(countW, nameW, valueW, lastKernelW int, hasLastKernel bool) string {
- if hasLastKernel {
- return fmt.Sprintf("| %%%ds | %%%ds | %%%ds | %%%ds |", countW, nameW, valueW, lastKernelW)
- }
- return fmt.Sprintf("| %%%ds | %%%ds | %%%ds |", countW, nameW, valueW)
-}
-
-func (r reportBuilder) buildReportBody(rows []tableRow, fmtStr string, hasLastKernel bool) string {
- var b strings.Builder
- for _, row := range rows {
- if hasLastKernel {
- b.WriteString(fmt.Sprintf(fmtStr+"\n", row.Pos, row.Name, row.Value, row.LastKernel))
- } else {
- b.WriteString(fmt.Sprintf(fmtStr+"\n", row.Pos, row.Name, row.Value))
- }
- }
- return b.String()
-}