| 1 | // Package report provides human-facing implementations of the legatus Reporter |
| 2 | // and Prompter interfaces: a text progress reporter and CLI/auto prompters. |
| 3 | // These are the injectable interactivity/progress impls (PC2): self-contained, |
| 4 | // no globals, no direct os.Stdin/os.Stdout access — all I/O flows through the |
| 5 | // configured readers and writers. |
| 6 | package report |
| 7 | |
| 8 | import ( |
| 9 | "fmt" |
| 10 | "io" |
| 11 | |
| 12 | "go.bigb.es/auxilia/legatus" |
| 13 | ) |
| 14 | |
| 15 | var _ legatus.Reporter = (*TextReporter)(nil) |
| 16 | |
| 17 | // TextReporter writes per-step progress lines and a final summary to W. When |
| 18 | // Color is set, glyphs are wrapped in ANSI color codes. |
| 19 | type TextReporter struct { |
| 20 | W io.Writer |
| 21 | Color bool |
| 22 | } |
| 23 | |
| 24 | // ANSI color codes used when Color is enabled. |
| 25 | const ( |
| 26 | ansiReset = "\x1b[0m" |
| 27 | ansiGreen = "\x1b[32m" |
| 28 | ansiYellow = "\x1b[33m" |
| 29 | ansiRed = "\x1b[31m" |
| 30 | ) |
| 31 | |
| 32 | // FlowStart announces the start of a flow on a host. |
| 33 | func (r *TextReporter) FlowStart(host, flow string) { |
| 34 | fmt.Fprintf(r.W, "%s: flow %s\n", host, flow) |
| 35 | } |
| 36 | |
| 37 | // StepStart announces a step about to run on a host. |
| 38 | func (r *TextReporter) StepStart(host, step string) { |
| 39 | fmt.Fprintf(r.W, "%s: %s ...\n", host, step) |
| 40 | } |
| 41 | |
| 42 | // StepDone reports the outcome of a step with a status glyph. |
| 43 | func (r *TextReporter) StepDone(host string, res legatus.StepResult) { |
| 44 | glyph := r.glyph(res.Status) |
| 45 | if res.Err != nil { |
| 46 | fmt.Fprintf(r.W, "%s: %s %s (%s): %v\n", host, glyph, res.Step, res.Status, res.Err) |
| 47 | return |
| 48 | } |
| 49 | fmt.Fprintf(r.W, "%s: %s %s (%s)\n", host, glyph, res.Step, res.Status) |
| 50 | } |
| 51 | |
| 52 | // FlowDone reports the aggregate status of a flow on a host. |
| 53 | func (r *TextReporter) FlowDone(host string, res legatus.FlowResult) { |
| 54 | fmt.Fprintf(r.W, "%s: flow %s done (%s)\n", host, res.Flow, res.Status) |
| 55 | } |
| 56 | |
| 57 | // RunDone writes the final tally across all hosts. |
| 58 | func (r *TextReporter) RunDone(res legatus.RunResult) { |
| 59 | ok, changed, skipped, failed := res.Counts() |
| 60 | fmt.Fprintf(r.W, "ok=%d changed=%d skipped=%d failed=%d\n", ok, changed, skipped, failed) |
| 61 | } |
| 62 | |
| 63 | // glyph maps a status to its display glyph, applying ANSI color when enabled. |
| 64 | func (r *TextReporter) glyph(s legatus.Status) string { |
| 65 | var g, c string |
| 66 | switch s { |
| 67 | case legatus.StatusChanged, legatus.StatusWouldChange, legatus.StatusOK: |
| 68 | g, c = "✓", ansiGreen |
| 69 | case legatus.StatusSkipped: |
| 70 | g, c = "→", ansiYellow |
| 71 | case legatus.StatusFailed: |
| 72 | g, c = "✗", ansiRed |
| 73 | default: |
| 74 | g, c = "?", "" |
| 75 | } |
| 76 | if r.Color && c != "" { |
| 77 | return c + g + ansiReset |
| 78 | } |
| 79 | return g |
| 80 | } |
| 81 | |