File: table.go

package info (click to toggle)
golang-github-gosuri-uitable 0.0~git20170830.36ee7e94-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, buster, experimental, sid
  • size: 116 kB
  • sloc: makefile: 8
file content (203 lines) | stat: -rw-r--r-- 4,620 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// Package uitable provides a decorator for formating data as a table
package uitable

import (
	"fmt"
	"strings"
	"sync"

	"github.com/gosuri/uitable/util/strutil"
	"github.com/gosuri/uitable/util/wordwrap"
	"github.com/mattn/go-runewidth"
)

// Separator is the default column seperator
var Separator = "\t"

// Table represents a decorator that renders the data in formatted in a table
type Table struct {
	// Rows is the collection of rows in the table
	Rows []*Row

	// MaxColWidth is the maximum allowed width for cells in the table
	MaxColWidth uint

	// Wrap when set to true wraps the contents of the columns when the length exceeds the MaxColWidth
	Wrap bool

	// Separator is the seperator for columns in the table. Default is "\t"
	Separator string

	mtx        *sync.RWMutex
	rightAlign map[int]bool
}

// New returns a new Table with default values
func New() *Table {
	return &Table{
		Separator:  Separator,
		mtx:        new(sync.RWMutex),
		rightAlign: map[int]bool{},
	}
}

// AddRow adds a new row to the table
func (t *Table) AddRow(data ...interface{}) *Table {
	t.mtx.Lock()
	defer t.mtx.Unlock()
	r := NewRow(data...)
	t.Rows = append(t.Rows, r)
	return t
}

// Bytes returns the []byte value of table
func (t *Table) Bytes() []byte {
	return []byte(t.String())
}

func (t *Table) RightAlign(col int) {
	t.mtx.Lock()
	t.rightAlign[col] = true
	t.mtx.Unlock()
}

// String returns the string value of table
func (t *Table) String() string {
	t.mtx.RLock()
	defer t.mtx.RUnlock()

	if len(t.Rows) == 0 {
		return ""
	}

	// determine the width for each column (cell in a row)
	var colwidths []uint
	for _, row := range t.Rows {
		for i, cell := range row.Cells {
			// resize colwidth array
			if i+1 > len(colwidths) {
				colwidths = append(colwidths, 0)
			}
			cellwidth := cell.LineWidth()
			if t.MaxColWidth != 0 && cellwidth > t.MaxColWidth {
				cellwidth = t.MaxColWidth
			}

			if cellwidth > colwidths[i] {
				colwidths[i] = cellwidth
			}
		}
	}

	var lines []string
	for _, row := range t.Rows {
		row.Separator = t.Separator
		for i, cell := range row.Cells {
			cell.Width = colwidths[i]
			cell.Wrap = t.Wrap
			cell.RightAlign = t.rightAlign[i]
		}
		lines = append(lines, row.String())
	}
	return strutil.Join(lines, "\n")
}

// Row represents a row in a table
type Row struct {
	// Cells is the group of cell for the row
	Cells []*Cell

	// Separator for tabular columns
	Separator string
}

// NewRow returns a new Row and adds the data to the row
func NewRow(data ...interface{}) *Row {
	r := &Row{Cells: make([]*Cell, len(data))}
	for i, d := range data {
		r.Cells[i] = &Cell{Data: d}
	}
	return r
}

// String returns the string representation of the row
func (r *Row) String() string {
	// get the max number of lines for each cell
	var lc int // line count
	for _, cell := range r.Cells {
		if clc := len(strings.Split(cell.String(), "\n")); clc > lc {
			lc = clc
		}
	}

	// allocate a two-dimentional array of cells for each line and add size them
	cells := make([][]*Cell, lc)
	for x := 0; x < lc; x++ {
		cells[x] = make([]*Cell, len(r.Cells))
		for y := 0; y < len(r.Cells); y++ {
			cells[x][y] = &Cell{Width: r.Cells[y].Width}
		}
	}

	// insert each line in a cell as new cell in the cells array
	for y, cell := range r.Cells {
		lines := strings.Split(cell.String(), "\n")
		for x, line := range lines {
			cells[x][y].Data = line
		}
	}

	// format each line
	lines := make([]string, lc)
	for x := range lines {
		line := make([]string, len(cells[x]))
		for y := range cells[x] {
			line[y] = cells[x][y].String()
		}
		lines[x] = strutil.Join(line, r.Separator)
	}
	return strutil.Join(lines, "\n")
}

// Cell represents a column in a row
type Cell struct {
	// Width is the width of the cell
	Width uint

	// Wrap when true wraps the contents of the cell when the lenght exceeds the width
	Wrap bool

	// RightAlign when true aligns contents to the right
	RightAlign bool

	// Data is the cell data
	Data interface{}
}

// LineWidth returns the max width of all the lines in a cell
func (c *Cell) LineWidth() uint {
	width := 0
	for _, s := range strings.Split(c.String(), "\n") {
		w := runewidth.StringWidth(s)
		if w > width {
			width = w
		}
	}
	return uint(width)
}

// String returns the string formated representation of the cell
func (c *Cell) String() string {
	if c.Data == nil {
		return strutil.PadLeft(" ", int(c.Width), ' ')
	}
	s := fmt.Sprintf("%v", c.Data)
	if c.Width > 0 {
		if c.Wrap && uint(len(s)) > c.Width {
			return wordwrap.WrapString(s, c.Width)
		} else {
			return strutil.Resize(s, c.Width, c.RightAlign)
		}
	}
	return s
}