github.com/puellanivis/breton@v0.2.16/lib/display/tables/display.go (about) 1 package tables 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "strings" 8 ) 9 10 func (d *Divider) writeDivider(wr io.Writer, widths []int) (n int64, err error) { 11 line := new(bytes.Buffer) 12 13 if d.Left != "" { 14 line.WriteString(d.Left) 15 } 16 17 switch { 18 case d.Bar == "": 19 var cols []string 20 21 for _, width := range widths { 22 if width < 0 { 23 width = -width 24 } 25 26 cols = append(cols, strings.Repeat(d.Space, width)) 27 } 28 29 line.WriteString(strings.Join(cols, d.Space)) 30 31 case d.Space != "": 32 var cols []string 33 34 for _, width := range widths { 35 if width < 0 { 36 width = -width 37 } 38 39 cols = append(cols, strings.Repeat(d.Space, width+2)) 40 } 41 42 line.WriteString(strings.Join(cols, d.Bar)) 43 } 44 45 if d.Right != "" { 46 line.WriteString(d.Right) 47 } 48 49 if line.Len() == 0 { 50 return 0, nil 51 } 52 53 line.WriteByte('\n') 54 return io.Copy(wr, line) 55 } 56 57 func (d *Divider) scale(width int, text string, fn func(string) int) string { 58 if width == 0 || d.Space == "" { 59 return text 60 } 61 62 l := len(text) 63 64 if fn != nil { 65 l = fn(text) 66 } 67 68 if width < 0 { 69 padding := strings.Repeat(d.Space, -width-l) 70 return fmt.Sprint(padding, text) 71 } 72 73 padding := strings.Repeat(d.Space, width-l) 74 return fmt.Sprint(text, padding) 75 } 76 77 // writeRow buffers the whole line together, then makes a single wr.Write of the rendered row. 78 func (f *Format) writeRow(wr io.Writer, cols []string) (n int64, err error) { 79 line := new(bytes.Buffer) 80 81 if f.Inner == nil { 82 // if there is no defined Inner format, just dump it raw. 83 for _, col := range cols { 84 line.WriteString(col) 85 } 86 87 line.WriteByte('\n') 88 return io.Copy(wr, line) 89 } 90 91 if f.Inner.Left != "" { 92 line.WriteString(f.Inner.Left) 93 line.WriteString(f.Inner.Space) 94 } 95 96 var sep = f.Inner.Space 97 if f.Inner.Bar != "" { 98 sep = fmt.Sprint(f.Inner.Space, f.Inner.Bar, f.Inner.Space) 99 } 100 line.WriteString(strings.Join(cols, sep)) 101 102 if f.Inner.Right != "" { 103 line.WriteString(f.Inner.Space) 104 line.WriteString(f.Inner.Right) 105 } 106 107 if line.Len() == 0 { 108 // if line is empty, don’t perform any Write at all. 109 // Otherwise we would put a newline in. 110 return 0, nil 111 } 112 113 line.WriteByte('\n') 114 return io.Copy(wr, line) 115 } 116 117 func (f *Format) writeRowScale(wr io.Writer, row []string, widths []int) (n int64, err error) { 118 var cols []string 119 120 if f.Inner.Right == "" { 121 w := make([]int, len(row)-1) 122 copy(w, widths) 123 widths = w 124 widths = append(widths, 0) 125 } 126 127 for i, width := range widths { 128 cols = append(cols, f.Inner.scale(width, row[i], f.WidthFunc)) 129 } 130 131 return f.writeRow(wr, cols) 132 } 133 134 // WriteSimple takes only a 2D slice to fill in the table. It uses the Default format. 135 func WriteSimple(wr io.Writer, table Table) error { 136 return Default.WriteSimple(wr, table) 137 } 138 139 // WriteSimple takes only a 2D slice to fill in the table. 140 func (f *Format) WriteSimple(wr io.Writer, table Table) error { 141 widths := table.widths(f.Inner.Space != "", f.WidthFunc) 142 143 if f.Upper != nil { 144 if _, err := f.Upper.writeDivider(wr, widths); err != nil { 145 return err 146 } 147 } 148 149 for _, row := range table { 150 if len(row) < 1 { 151 if f.Middle != nil { 152 if _, err := f.Middle.writeDivider(wr, widths); err != nil { 153 return err 154 } 155 } 156 157 continue 158 } 159 160 if f.Inner.Space == "" { 161 if _, err := f.writeRow(wr, row); err != nil { 162 return err 163 } 164 165 continue 166 } 167 168 if f.Inner.Right != "" && len(row) < len(widths) { 169 // in this case, we need to pad the available rows to match the rest of the table. 170 l := len(widths) - len(row) 171 row = append(row, make([]string, l)...) 172 } 173 174 if _, err := f.writeRowScale(wr, row, widths); err != nil { 175 return err 176 } 177 } 178 179 if f.Lower != nil { 180 if _, err := f.Lower.writeDivider(wr, widths); err != nil { 181 return err 182 } 183 } 184 185 return nil 186 } 187 188 // WriteMulti writes the data given out it uses the Default format 189 func WriteMulti(wr io.Writer, table [][][]string) error { 190 return Default.WriteMulti(wr, table) 191 } 192 193 func rowHeight(row [][]string) int { 194 var height int 195 196 for _, col := range row { 197 if height < len(col) { 198 height = len(col) 199 } 200 } 201 202 return height 203 } 204 205 // WriteMulti writes the table given out according to the Format. Each row is checked for height > 1, and if true, it inserts additional lines in a normal 2D Table, populated with available data, and with empty cells for columns where its height is less than the height of the row. It also inserts a new empty row after any row that is height > 1 if the next row is height > 1. 206 func (f *Format) WriteMulti(wr io.Writer, table [][][]string) error { 207 var tbl Table 208 var last int 209 210 for _, row := range table { 211 height := rowHeight(row) 212 213 if height > 1 || last > 1 { 214 tbl = append(tbl, []string{}) 215 } 216 217 for i := 0; i < height; i++ { 218 var r []string 219 220 for _, col := range row { 221 var c string 222 223 if i < len(col) { 224 c = col[i] 225 } 226 227 r = append(r, c) 228 } 229 230 tbl = append(tbl, r) 231 } 232 233 last = height 234 } 235 236 return f.WriteSimple(wr, tbl) 237 }