gogcli/internal/ui/ui.go
Peter Steinberger 47c06796b0 fix(lint): tidy cmd/ui lint findings (#35)
Thanks @salmonumbrella.

Co-authored-by: salmonumbrella <salmonumbrella@users.noreply.github.com>
2026-01-09 04:34:31 +01:00

137 lines
2.8 KiB
Go

package ui
import (
"context"
"fmt"
"io"
"os"
"strings"
"github.com/muesli/termenv"
)
type Options struct {
Stdout io.Writer
Stderr io.Writer
Color string // auto|always|never
}
const colorNever = "never"
type UI struct {
out *Printer
err *Printer
}
type ParseError struct{ msg string }
func (e *ParseError) Error() string { return e.msg }
func New(opts Options) (*UI, error) {
if opts.Stdout == nil {
opts.Stdout = os.Stdout
}
if opts.Stderr == nil {
opts.Stderr = os.Stderr
}
colorMode := strings.ToLower(strings.TrimSpace(opts.Color))
if colorMode == "" {
colorMode = "auto"
}
if colorMode != "auto" && colorMode != "always" && colorMode != colorNever {
return nil, &ParseError{msg: "invalid --color (expected auto|always|never)"}
}
out := termenv.NewOutput(opts.Stdout, termenv.WithProfile(termenv.EnvColorProfile()))
errOut := termenv.NewOutput(opts.Stderr, termenv.WithProfile(termenv.EnvColorProfile()))
outProfile := chooseProfile(out.Profile, colorMode)
errProfile := chooseProfile(errOut.Profile, colorMode)
return &UI{
out: newPrinter(out, outProfile),
err: newPrinter(errOut, errProfile),
}, nil
}
func chooseProfile(detected termenv.Profile, mode string) termenv.Profile {
if termenv.EnvNoColor() {
return termenv.Ascii
}
switch mode {
case colorNever:
return termenv.Ascii
case "always":
return termenv.TrueColor
default:
return detected
}
}
func (u *UI) Out() *Printer { return u.out }
func (u *UI) Err() *Printer { return u.err }
type Printer struct {
o *termenv.Output
profile termenv.Profile
}
func newPrinter(o *termenv.Output, profile termenv.Profile) *Printer {
return &Printer{o: o, profile: profile}
}
func (p *Printer) ColorEnabled() bool { return p.profile != termenv.Ascii }
func (p *Printer) line(s string) {
_, _ = io.WriteString(p.o, s+"\n")
}
func (p *Printer) printf(format string, args ...any) {
p.line(fmt.Sprintf(format, args...))
}
func (p *Printer) Print(msg string) {
_, _ = io.WriteString(p.o, msg)
}
func (p *Printer) Successf(format string, args ...any) {
msg := fmt.Sprintf(format, args...)
if p.ColorEnabled() {
msg = termenv.String(msg).Foreground(p.profile.Color("#22c55e")).String()
}
p.line(msg)
}
func (p *Printer) Error(msg string) {
if p.ColorEnabled() {
msg = termenv.String(msg).Foreground(p.profile.Color("#ef4444")).String()
}
p.line(msg)
}
func (p *Printer) Errorf(format string, args ...any) { p.Error(fmt.Sprintf(format, args...)) }
func (p *Printer) Printf(format string, args ...any) { p.printf(format, args...) }
func (p *Printer) Println(msg string) { p.line(msg) }
type ctxKey struct{}
func WithUI(ctx context.Context, u *UI) context.Context {
return context.WithValue(ctx, ctxKey{}, u)
}
func FromContext(ctx context.Context) *UI {
v := ctx.Value(ctxKey{})
if v == nil {
return nil
}
u, _ := v.(*UI)
return u
}