table.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // Package uitable provides a decorator for formating data as a table
  2. package uitable
  3. import (
  4. "fmt"
  5. "strings"
  6. "sync"
  7. "github.com/gosuri/uitable/util/strutil"
  8. "github.com/gosuri/uitable/util/wordwrap"
  9. "github.com/mattn/go-runewidth"
  10. )
  11. // Separator is the default column seperator
  12. var Separator = "\t"
  13. // Table represents a decorator that renders the data in formatted in a table
  14. type Table struct {
  15. // Rows is the collection of rows in the table
  16. Rows []*Row
  17. // MaxColWidth is the maximum allowed width for cells in the table
  18. MaxColWidth uint
  19. // Wrap when set to true wraps the contents of the columns when the length exceeds the MaxColWidth
  20. Wrap bool
  21. // Separator is the seperator for columns in the table. Default is "\t"
  22. Separator string
  23. mtx *sync.RWMutex
  24. rightAlign map[int]bool
  25. }
  26. // New returns a new Table with default values
  27. func New() *Table {
  28. return &Table{
  29. Separator: Separator,
  30. mtx: new(sync.RWMutex),
  31. rightAlign: map[int]bool{},
  32. }
  33. }
  34. // AddRow adds a new row to the table
  35. func (t *Table) AddRow(data ...interface{}) *Table {
  36. t.mtx.Lock()
  37. defer t.mtx.Unlock()
  38. r := NewRow(data...)
  39. t.Rows = append(t.Rows, r)
  40. return t
  41. }
  42. // Bytes returns the []byte value of table
  43. func (t *Table) Bytes() []byte {
  44. return []byte(t.String())
  45. }
  46. func (t *Table) RightAlign(col int) {
  47. t.mtx.Lock()
  48. t.rightAlign[col] = true
  49. t.mtx.Unlock()
  50. }
  51. // String returns the string value of table
  52. func (t *Table) String() string {
  53. t.mtx.RLock()
  54. defer t.mtx.RUnlock()
  55. if len(t.Rows) == 0 {
  56. return ""
  57. }
  58. // determine the width for each column (cell in a row)
  59. var colwidths []uint
  60. for _, row := range t.Rows {
  61. for i, cell := range row.Cells {
  62. // resize colwidth array
  63. if i+1 > len(colwidths) {
  64. colwidths = append(colwidths, 0)
  65. }
  66. cellwidth := cell.LineWidth()
  67. if t.MaxColWidth != 0 && cellwidth > t.MaxColWidth {
  68. cellwidth = t.MaxColWidth
  69. }
  70. if cellwidth > colwidths[i] {
  71. colwidths[i] = cellwidth
  72. }
  73. }
  74. }
  75. var lines []string
  76. for _, row := range t.Rows {
  77. row.Separator = t.Separator
  78. for i, cell := range row.Cells {
  79. cell.Width = colwidths[i]
  80. cell.Wrap = t.Wrap
  81. cell.RightAlign = t.rightAlign[i]
  82. }
  83. lines = append(lines, row.String())
  84. }
  85. return strutil.Join(lines, "\n")
  86. }
  87. // Row represents a row in a table
  88. type Row struct {
  89. // Cells is the group of cell for the row
  90. Cells []*Cell
  91. // Separator for tabular columns
  92. Separator string
  93. }
  94. // NewRow returns a new Row and adds the data to the row
  95. func NewRow(data ...interface{}) *Row {
  96. r := &Row{Cells: make([]*Cell, len(data))}
  97. for i, d := range data {
  98. r.Cells[i] = &Cell{Data: d}
  99. }
  100. return r
  101. }
  102. // String returns the string representation of the row
  103. func (r *Row) String() string {
  104. // get the max number of lines for each cell
  105. var lc int // line count
  106. for _, cell := range r.Cells {
  107. if clc := len(strings.Split(cell.String(), "\n")); clc > lc {
  108. lc = clc
  109. }
  110. }
  111. // allocate a two-dimentional array of cells for each line and add size them
  112. cells := make([][]*Cell, lc)
  113. for x := 0; x < lc; x++ {
  114. cells[x] = make([]*Cell, len(r.Cells))
  115. for y := 0; y < len(r.Cells); y++ {
  116. cells[x][y] = &Cell{Width: r.Cells[y].Width}
  117. }
  118. }
  119. // insert each line in a cell as new cell in the cells array
  120. for y, cell := range r.Cells {
  121. lines := strings.Split(cell.String(), "\n")
  122. for x, line := range lines {
  123. cells[x][y].Data = line
  124. }
  125. }
  126. // format each line
  127. lines := make([]string, lc)
  128. for x := range lines {
  129. line := make([]string, len(cells[x]))
  130. for y := range cells[x] {
  131. line[y] = cells[x][y].String()
  132. }
  133. lines[x] = strutil.Join(line, r.Separator)
  134. }
  135. return strutil.Join(lines, "\n")
  136. }
  137. // Cell represents a column in a row
  138. type Cell struct {
  139. // Width is the width of the cell
  140. Width uint
  141. // Wrap when true wraps the contents of the cell when the lenght exceeds the width
  142. Wrap bool
  143. // RightAlign when true aligns contents to the right
  144. RightAlign bool
  145. // Data is the cell data
  146. Data interface{}
  147. }
  148. // LineWidth returns the max width of all the lines in a cell
  149. func (c *Cell) LineWidth() uint {
  150. width := 0
  151. for _, s := range strings.Split(c.String(), "\n") {
  152. w := runewidth.StringWidth(s)
  153. if w > width {
  154. width = w
  155. }
  156. }
  157. return uint(width)
  158. }
  159. // String returns the string formated representation of the cell
  160. func (c *Cell) String() string {
  161. if c.Data == nil {
  162. return strutil.PadLeft(" ", int(c.Width), ' ')
  163. }
  164. s := fmt.Sprintf("%v", c.Data)
  165. if c.Width > 0 {
  166. if c.Wrap && uint(len(s)) > c.Width {
  167. return wordwrap.WrapString(s, c.Width)
  168. } else {
  169. return strutil.Resize(s, c.Width, c.RightAlign)
  170. }
  171. }
  172. return s
  173. }