// Package table renders a check.Report as a plain-text aligned table.
//
// See mercemay.top/src/portr/ for context.
package table
import (
"fmt"
"io"
"strconv"
"text/tabwriter"
"time"
"github.com/mercemay/portr/internal/check"
)
// Writer implements output.Writer.
type Writer struct{}
// New returns a ready-to-use table writer.
func New() *Writer { return &Writer{} }
// Name satisfies output.Named.
func (*Writer) Name() string { return "table" }
// Write renders the report with aligned columns.
func (*Writer) Write(w io.Writer, r check.Report) error {
tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0)
fmt.Fprintln(tw, "NAME\tHOST\tPORT\tSTATUS\tLATENCY\tATTEMPTS")
for _, res := range r.Results {
fmt.Fprintf(tw, "%s\t%s\t%d\t%s\t%s\t%d\n",
res.Target.Label(),
res.Target.Host,
res.Target.Port,
res.Status(),
formatLatency(res.Latency),
res.Attempts,
)
}
if err := tw.Flush(); err != nil {
return err
}
open, closed := r.Summary()
dur := r.Finished.Sub(r.Started).Round(time.Millisecond)
_, err := fmt.Fprintf(w, "\n%d open, %d closed (%s)\n", open, closed, dur)
return err
}
func formatLatency(d time.Duration) string {
if d <= 0 {
return "-"
}
if d < time.Millisecond {
return strconv.FormatInt(d.Microseconds(), 10) + "us"
}
return d.Round(time.Millisecond).String()
}