pda/cmd/msg.go

95 lines
2.4 KiB
Go

package cmd
import (
"errors"
"fmt"
"os"
"strings"
"golang.org/x/term"
)
// hinted wraps an error with an actionable hint shown on a separate line.
type hinted struct {
err error
hint string
}
func (h hinted) Error() string { return h.err.Error() }
func (h hinted) Unwrap() error { return h.err }
func withHint(err error, hint string) error {
return hinted{err: err, hint: hint}
}
func stderrIsTerminal() bool {
return term.IsTerminal(int(os.Stderr.Fd()))
}
func stdoutIsTerminal() bool {
return term.IsTerminal(int(os.Stdout.Fd()))
}
// keyword returns a right-aligned, colored keyword (color only on TTY).
//
// FAIL red (stderr)
// hint dim (stderr)
// WARN yellow (stderr)
// ok green (stderr)
// ? cyan (stdout)
// > dim (stdout)
func keyword(code, word string, tty bool) string {
padded := fmt.Sprintf("%4s", word)
if tty {
return fmt.Sprintf("\033[%sm%s\033[0m", code, padded)
}
return padded
}
func printError(err error) {
fmt.Fprintf(os.Stderr, "%s %s\n", keyword("31", "FAIL", stderrIsTerminal()), err)
}
func printHint(format string, args ...any) {
msg := fmt.Sprintf(format, args...)
fmt.Fprintf(os.Stderr, "%s %s\n", keyword("2", "hint", stderrIsTerminal()), msg)
}
func warnf(format string, args ...any) {
msg := fmt.Sprintf(format, args...)
fmt.Fprintf(os.Stderr, "%s %s\n", keyword("33", "WARN", stderrIsTerminal()), msg)
}
func okf(format string, args ...any) {
msg := fmt.Sprintf(format, args...)
fmt.Fprintf(os.Stderr, "%s %s\n", keyword("32", "ok", stderrIsTerminal()), msg)
}
func promptf(format string, args ...any) {
msg := fmt.Sprintf(format, args...)
fmt.Fprintf(os.Stdout, "%s %s\n", keyword("36", "???", stdoutIsTerminal()), msg)
}
func progressf(format string, args ...any) {
msg := fmt.Sprintf(format, args...)
fmt.Fprintf(os.Stdout, "%s %s\n", keyword("2", ">", stdoutIsTerminal()), msg)
}
func scanln(dest *string) error {
fmt.Fprintf(os.Stdout, "%s ", keyword("2", "==>", stdoutIsTerminal()))
_, err := fmt.Scanln(dest)
return err
}
// printErrorWithHints prints the error and any hints found in the error chain.
func printErrorWithHints(err error) {
printError(err)
var h hinted
if errors.As(err, &h) {
printHint("%s", h.hint)
}
var nf errNotFound
if errors.As(err, &nf) && len(nf.suggestions) > 0 {
printHint("did you mean '%s'?", strings.Join(nf.suggestions, "', '"))
}
}