syft/cmd/syft/internal/ui/post_ui_event_writer.go
Alex Goodman f8b832e6c3
Switch UI to bubbletea (#1888)
* add bubbletea UI

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* swap pipeline to go 1.20.x and add attest guard for cosign binary

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* update note in developing.md about the required golang version

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix merge conflict for windows path handling

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* temp test for attest handler

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add addtional test iterations for background reader

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2023-07-06 09:00:46 -04:00

134 lines
3.2 KiB
Go

package ui
import (
"fmt"
"io"
"strings"
"github.com/charmbracelet/lipgloss"
"github.com/hashicorp/go-multierror"
"github.com/wagoodman/go-partybus"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/event"
"github.com/anchore/syft/syft/event/parsers"
)
type postUIEventWriter struct {
handles []postUIHandle
}
type postUIHandle struct {
respectQuiet bool
event partybus.EventType
writer io.Writer
dispatch eventWriter
}
type eventWriter func(io.Writer, ...partybus.Event) error
func newPostUIEventWriter(stdout, stderr io.Writer) *postUIEventWriter {
return &postUIEventWriter{
handles: []postUIHandle{
{
event: event.CLIReport,
respectQuiet: false,
writer: stdout,
dispatch: writeReports,
},
{
event: event.CLINotification,
respectQuiet: true,
writer: stderr,
dispatch: writeNotifications,
},
{
event: event.CLIAppUpdateAvailable,
respectQuiet: true,
writer: stderr,
dispatch: writeAppUpdate,
},
},
}
}
func (w postUIEventWriter) write(quiet bool, events ...partybus.Event) error {
var errs error
for _, h := range w.handles {
if quiet && h.respectQuiet {
continue
}
for _, e := range events {
if e.Type != h.event {
continue
}
if err := h.dispatch(h.writer, e); err != nil {
errs = multierror.Append(errs, err)
}
}
}
return errs
}
func writeReports(writer io.Writer, events ...partybus.Event) error {
var reports []string
for _, e := range events {
_, report, err := parsers.ParseCLIReport(e)
if err != nil {
log.WithFields("error", err).Warn("failed to gather final report")
continue
}
// remove all whitespace padding from the end of the report
reports = append(reports, strings.TrimRight(report, "\n ")+"\n")
}
// prevent the double new-line at the end of the report
report := strings.Join(reports, "\n")
if _, err := fmt.Fprint(writer, report); err != nil {
return fmt.Errorf("failed to write final report to stdout: %w", err)
}
return nil
}
func writeNotifications(writer io.Writer, events ...partybus.Event) error {
// 13 = high intensity magenta (ANSI 16 bit code)
style := lipgloss.NewStyle().Foreground(lipgloss.Color("13"))
for _, e := range events {
_, notification, err := parsers.ParseCLINotification(e)
if err != nil {
log.WithFields("error", err).Warn("failed to parse notification")
continue
}
if _, err := fmt.Fprintln(writer, style.Render(notification)); err != nil {
// don't let this be fatal
log.WithFields("error", err).Warn("failed to write final notifications")
}
}
return nil
}
func writeAppUpdate(writer io.Writer, events ...partybus.Event) error {
// 13 = high intensity magenta (ANSI 16 bit code) + italics
style := lipgloss.NewStyle().Foreground(lipgloss.Color("13")).Italic(true)
for _, e := range events {
notice, err := parsers.ParseCLIAppUpdateAvailable(e)
if err != nil {
log.WithFields("error", err).Warn("failed to parse app update notification")
continue
}
if _, err := fmt.Fprintln(writer, style.Render(notice)); err != nil {
// don't let this be fatal
log.WithFields("error", err).Warn("failed to write app update notification")
}
}
return nil
}