cli-utils/pkg/print/table/base.go

155 lines
3.9 KiB
Go

// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package table
import (
"fmt"
"io"
"strings"
"unicode/utf8"
"k8s.io/cli-runtime/pkg/genericclioptions"
pe "github.com/fluxcd/cli-utils/pkg/kstatus/polling/event"
"github.com/fluxcd/cli-utils/pkg/object"
"github.com/fluxcd/cli-utils/pkg/print/common"
)
// ColumnDefinition defines the columns that should be printed.
type ColumnDefinition interface {
Name() string
Header() string
Width() int
PrintResource(w io.Writer, width int, r Resource) (int, error)
}
// ResourceStates defines the interface that must be implemented
// by the object that provides information about the resources
// that should be printed.
type ResourceStates interface {
Resources() []Resource
Error() error
}
// Resource defines the interface that each of the Resource
// objects must implement.
type Resource interface {
Identifier() object.ObjMetadata
ResourceStatus() *pe.ResourceStatus
SubResources() []Resource
}
// BaseTablePrinter provides functionality for printing information
// about a set of resources into a table format.
// The printer will print to the Out stream defined in IOStreams,
// and will print into the format defined by the Column definitions.
type BaseTablePrinter struct {
IOStreams genericclioptions.IOStreams
Columns []ColumnDefinition
}
// PrintTable prints the resources defined in ResourceStates. It will
// print subresources if they exist.
// moveUpCount defines how many lines the printer should move up
// before starting printing. The return value is how many lines
// were printed.
func (t *BaseTablePrinter) PrintTable(rs ResourceStates,
moveUpCount int) int {
for i := 0; i < moveUpCount; i++ {
t.moveUp()
t.eraseCurrentLine()
}
linePrintCount := 0
for i, column := range t.Columns {
format := fmt.Sprintf("%%-%ds", column.Width())
t.printOrDie(format, column.Header())
if i == len(t.Columns)-1 {
t.printOrDie("\n")
linePrintCount++
} else {
t.printOrDie(" ")
}
}
for _, resource := range rs.Resources() {
for i, column := range t.Columns {
written, err := column.PrintResource(t.IOStreams.Out, column.Width(), resource)
if err != nil {
panic(err)
}
remainingSpace := column.Width() - written
t.printOrDie("%s", strings.Repeat(" ", remainingSpace))
if i == len(t.Columns)-1 {
t.printOrDie("\n")
linePrintCount++
} else {
t.printOrDie(" ")
}
}
linePrintCount += t.printSubTable(resource.SubResources(), "")
}
return linePrintCount
}
// printSubTable prints out any subresources that belong to the
// top-level resources. This function takes care of printing the correct tree
// structure and indentation.
func (t *BaseTablePrinter) printSubTable(resources []Resource,
prefix string) int {
linePrintCount := 0
for j, resource := range resources {
for i, column := range t.Columns {
availableWidth := column.Width()
if column.Name() == "resource" {
if j < len(resources)-1 {
t.printOrDie("%s", prefix+`├─ `)
} else {
t.printOrDie("%s", prefix+`└─ `)
}
availableWidth -= utf8.RuneCountInString(prefix) + 3
}
written, err := column.PrintResource(t.IOStreams.Out,
availableWidth, resource)
if err != nil {
panic(err)
}
remainingSpace := availableWidth - written
t.printOrDie("%s", strings.Repeat(" ", remainingSpace))
if i == len(t.Columns)-1 {
t.printOrDie("\n")
linePrintCount++
} else {
t.printOrDie(" ")
}
}
var prefix string
if j < len(resources)-1 {
prefix = ``
} else {
prefix = " "
}
linePrintCount += t.printSubTable(resource.SubResources(), prefix)
}
return linePrintCount
}
func (t *BaseTablePrinter) printOrDie(format string, a ...interface{}) {
_, err := fmt.Fprintf(t.IOStreams.Out, format, a...)
if err != nil {
panic(err)
}
}
func (t *BaseTablePrinter) moveUp() {
t.printOrDie("%c[%dA", common.ESC, 1)
}
func (t *BaseTablePrinter) eraseCurrentLine() {
t.printOrDie("%c[2K\r", common.ESC)
}