github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/columns/formatter/textcolumns/output.go (about) 1 // Copyright 2022-2023 The Inspektor Gadget 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 textcolumns 16 17 import ( 18 "bytes" 19 "io" 20 "strings" 21 22 "github.com/inspektor-gadget/inspektor-gadget/pkg/columns" 23 24 "github.com/inspektor-gadget/inspektor-gadget/pkg/columns/ellipsis" 25 ) 26 27 func (tf *TextColumnsFormatter[T]) setFormatter(column *Column[T]) { 28 ff := columns.GetFieldAsStringExt[T](column.col, 'f', column.col.Precision) 29 column.formatter = func(entry *T) string { 30 return tf.buildFixedString(ff(entry), column.calculatedWidth, column.col.EllipsisType, column.col.Alignment) 31 } 32 } 33 34 func (tf *TextColumnsFormatter[T]) buildFixedString(s string, length int, ellipsisType ellipsis.EllipsisType, alignment columns.Alignment) string { 35 if length <= 0 { 36 return "" 37 } 38 39 if !tf.options.ShouldTruncate { 40 return s 41 } 42 43 rs := []rune(s) 44 45 shortened := ellipsis.Shorten(rs, length, ellipsisType) 46 if len(shortened) == length { 47 return string(shortened) 48 } 49 if alignment == columns.AlignLeft { 50 return string(shortened) + tf.fillString[0:length-len(shortened)] 51 } 52 return tf.fillString[0:length-len(shortened)] + string(shortened) 53 } 54 55 // FormatEntry returns an entry as a formatted string, respecting the given formatting settings 56 func (tf *TextColumnsFormatter[T]) FormatEntry(entry *T) string { 57 if entry == nil { 58 return "" 59 } 60 61 var row strings.Builder 62 for i, col := range tf.showColumns { 63 if i > 0 { 64 row.WriteString(tf.options.ColumnDivider) 65 } 66 row.WriteString(col.formatter(entry)) 67 } 68 return row.String() 69 } 70 71 // FormatHeader returns the formatted header line with all visible column names, separated by ColumnDivider 72 func (tf *TextColumnsFormatter[T]) FormatHeader() string { 73 tf.AdjustWidthsToScreen() 74 var row strings.Builder 75 for i, column := range tf.showColumns { 76 if i > 0 { 77 row.WriteString(tf.options.ColumnDivider) 78 } 79 name := column.col.Name 80 switch tf.options.HeaderStyle { 81 case HeaderStyleUppercase: 82 name = strings.ToUpper(name) 83 case HeaderStyleLowercase: 84 name = strings.ToLower(name) 85 } 86 row.WriteString(tf.buildFixedString(name, column.calculatedWidth, ellipsis.End, column.col.Alignment)) 87 } 88 return row.String() 89 } 90 91 // FormatRowDivider returns a string that repeats the defined RowDivider until the total length of a row is reached 92 func (tf *TextColumnsFormatter[T]) FormatRowDivider() string { 93 if tf.options.RowDivider == DividerNone { 94 return "" 95 } 96 var row strings.Builder 97 rowDividerLen := 0 98 for i, col := range tf.showColumns { 99 if i > 0 { 100 rowDividerLen += len([]rune(tf.options.ColumnDivider)) 101 } 102 rowDividerLen += col.calculatedWidth 103 } 104 for i := 0; i < rowDividerLen; i += len([]rune(tf.options.RowDivider)) { 105 row.WriteString(tf.options.RowDivider) 106 } 107 return string([]rune(row.String())[:rowDividerLen]) 108 } 109 110 // FormatTable returns header, divider and the formatted entries with the current settings as a string 111 func (tf *TextColumnsFormatter[T]) FormatTable(entries []*T) string { 112 buf := bytes.NewBuffer(nil) 113 _ = tf.WriteTable(buf, entries) 114 out := buf.Bytes() 115 return string(out[:len(out)-1]) 116 } 117 118 // WriteTable writes header, divider and the formatted entries with the current settings to writer 119 func (tf *TextColumnsFormatter[T]) WriteTable(writer io.Writer, entries []*T) error { 120 _, err := writer.Write([]byte(tf.FormatHeader())) 121 if err != nil { 122 return err 123 } 124 _, err = writer.Write([]byte("\n")) 125 if err != nil { 126 return err 127 } 128 if tf.options.RowDivider != DividerNone { 129 _, err = writer.Write([]byte(tf.FormatRowDivider())) 130 if err != nil { 131 return err 132 } 133 _, err = writer.Write([]byte("\n")) 134 if err != nil { 135 return err 136 } 137 } 138 for _, entry := range entries { 139 _, err = writer.Write([]byte(tf.FormatEntry(entry))) 140 if err != nil { 141 return err 142 } 143 _, err = writer.Write([]byte("\n")) 144 if err != nil { 145 return err 146 } 147 } 148 return nil 149 }