Replace golang.org/x/term with the already-depended-on go-isatty for TTY detection, matching the pattern in non_interactive_prompter.go. Add tests for InteractiveProgress Start/Finish and Update methods. Run go mod tidy to clean up stale go.sum entries. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
93 lines
2.1 KiB
Go
93 lines
2.1 KiB
Go
package ui
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
|
|
"github.com/mattn/go-isatty"
|
|
)
|
|
|
|
// Progress abstracts output for both interactive and scripted use.
|
|
type Progress interface {
|
|
Start(label string)
|
|
Update(current, total int64)
|
|
Finish(label string)
|
|
Log(level, msg string, args ...any)
|
|
}
|
|
|
|
// QuietProgress writes structured log lines. For cron, pipes, --quiet.
|
|
type QuietProgress struct {
|
|
w io.Writer
|
|
}
|
|
|
|
func NewQuietProgress(w io.Writer) *QuietProgress {
|
|
return &QuietProgress{w: w}
|
|
}
|
|
|
|
func (q *QuietProgress) Start(label string) {
|
|
fmt.Fprintf(q.w, "[START] %s\n", label)
|
|
}
|
|
|
|
func (q *QuietProgress) Update(current, total int64) {
|
|
if total > 0 {
|
|
fmt.Fprintf(q.w, "[PROGRESS] %d/%d\n", current, total)
|
|
}
|
|
}
|
|
|
|
func (q *QuietProgress) Finish(label string) {
|
|
fmt.Fprintf(q.w, "[DONE] %s\n", label)
|
|
}
|
|
|
|
func (q *QuietProgress) Log(level, msg string, args ...any) {
|
|
fmt.Fprintf(q.w, "[%s] %s", level, msg)
|
|
for i := 0; i+1 < len(args); i += 2 {
|
|
fmt.Fprintf(q.w, " %v=%v", args[i], args[i+1])
|
|
}
|
|
fmt.Fprintln(q.w)
|
|
}
|
|
|
|
// InteractiveProgress uses simple terminal output for TTY sessions.
|
|
type InteractiveProgress struct {
|
|
w io.Writer
|
|
}
|
|
|
|
func NewInteractiveProgress(w io.Writer) *InteractiveProgress {
|
|
return &InteractiveProgress{w: w}
|
|
}
|
|
|
|
func (p *InteractiveProgress) Start(label string) {
|
|
fmt.Fprintf(p.w, "→ %s\n", label)
|
|
}
|
|
|
|
func (p *InteractiveProgress) Update(current, total int64) {
|
|
if total > 0 {
|
|
pct := current * 100 / total
|
|
fmt.Fprintf(p.w, "\r %d%%", pct)
|
|
}
|
|
}
|
|
|
|
func (p *InteractiveProgress) Finish(label string) {
|
|
fmt.Fprintf(p.w, "\r✓ %s\n", label)
|
|
}
|
|
|
|
func (p *InteractiveProgress) Log(level, msg string, args ...any) {
|
|
fmt.Fprintf(p.w, " %s", msg)
|
|
for i := 0; i+1 < len(args); i += 2 {
|
|
fmt.Fprintf(p.w, " %v=%v", args[i], args[i+1])
|
|
}
|
|
fmt.Fprintln(p.w)
|
|
}
|
|
|
|
// IsTTY returns true if the given file descriptor is a terminal.
|
|
func IsTTY(fd uintptr) bool {
|
|
return isatty.IsTerminal(fd) || isatty.IsCygwinTerminal(fd)
|
|
}
|
|
|
|
// DefaultProgress returns InteractiveProgress for TTYs, QuietProgress otherwise.
|
|
func DefaultProgress() Progress {
|
|
if IsTTY(os.Stdout.Fd()) {
|
|
return NewInteractiveProgress(os.Stdout)
|
|
}
|
|
return NewQuietProgress(os.Stdout)
|
|
}
|