istio.io/istio@v0.0.0-20240520182934-d79c90f27776/istioctl/pkg/writer/table/writer.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package table
    16  
    17  import (
    18  	"fmt"
    19  	"io"
    20  	"strings"
    21  	"unicode/utf8"
    22  
    23  	"github.com/fatih/color"
    24  )
    25  
    26  type ColoredTableWriter struct {
    27  	writer     io.Writer
    28  	header     Row
    29  	rows       []Row
    30  	addRowFunc func(obj interface{}) Row
    31  }
    32  
    33  func NewStyleWriter(writer io.Writer) *ColoredTableWriter {
    34  	return &ColoredTableWriter{
    35  		writer: writer,
    36  		rows:   make([]Row, 0),
    37  		header: Row{},
    38  	}
    39  }
    40  
    41  type BuildRowFunc func(obj interface{}) Row
    42  
    43  type Cell struct {
    44  	Value      string
    45  	Attributes []color.Attribute
    46  }
    47  
    48  type Row struct {
    49  	Cells []Cell
    50  }
    51  
    52  func NewCell(value string, attributes ...color.Attribute) Cell {
    53  	attrs := append([]color.Attribute{}, attributes...)
    54  	return Cell{value, attrs}
    55  }
    56  
    57  func (cell Cell) String() string {
    58  	if len(cell.Attributes) == 0 {
    59  		return cell.Value
    60  	}
    61  	s := color.New(cell.Attributes...)
    62  	s.EnableColor()
    63  	return s.Sprintf("%s", cell.Value)
    64  }
    65  
    66  func (c *ColoredTableWriter) getTableOutput(allRows []Row) [][]Cell {
    67  	output := [][]Cell{}
    68  	if len(c.header.Cells) != 0 {
    69  		output = append(output, c.header.Cells)
    70  	}
    71  	for _, row := range allRows {
    72  		output = append(output, row.Cells)
    73  	}
    74  	return output
    75  }
    76  
    77  func (c *ColoredTableWriter) SetAddRowFunc(f func(obj interface{}) Row) {
    78  	c.addRowFunc = f
    79  }
    80  
    81  func (c *ColoredTableWriter) AddHeader(names ...string) {
    82  	cells := make([]Cell, 0)
    83  	for _, name := range names {
    84  		cells = append(cells, NewCell(name))
    85  	}
    86  	c.header = Row{Cells: cells}
    87  }
    88  
    89  func (c *ColoredTableWriter) AddRow(obj interface{}) {
    90  	c.rows = append(c.rows, c.addRowFunc(obj))
    91  }
    92  
    93  func (c *ColoredTableWriter) Flush() {
    94  	output := c.getTableOutput(c.rows)
    95  	if len(output) == 0 {
    96  		return
    97  	}
    98  	sep := getMaxWidths(output)
    99  	for _, row := range output {
   100  		for i, col := range row {
   101  			_, _ = fmt.Fprint(c.writer, col.String())
   102  			if i == len(row)-1 {
   103  				_, _ = fmt.Fprint(c.writer, "\n")
   104  			} else {
   105  				padAmount := sep[i] - utf8.RuneCount([]byte(col.Value)) + 2
   106  				_, _ = fmt.Fprint(c.writer, strings.Repeat(" ", padAmount))
   107  			}
   108  		}
   109  	}
   110  }
   111  
   112  func max(x, y int) int {
   113  	if x < y {
   114  		return y
   115  	}
   116  	return x
   117  }
   118  
   119  func getMaxWidths(output [][]Cell) []int {
   120  	widths := make([]int, len(output[0]))
   121  	for _, row := range output {
   122  		for i, col := range row {
   123  			widths[i] = max(widths[i], len(col.String()))
   124  		}
   125  	}
   126  	return widths
   127  }