File: table.go

package info (click to toggle)
golang-github-evilsocket-islazy 1.11.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 292 kB
  • sloc: javascript: 8; makefile: 3
file content (148 lines) | stat: -rw-r--r-- 3,139 bytes parent folder | download | duplicates (3)
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
package tui

import (
	"fmt"
	"io"
	"regexp"
	"strings"
	"unicode/utf8"
)

var ansi = regexp.MustCompile("\033\\[(?:[0-9]{1,3}(?:;[0-9]{1,3})*)?[m|K]")

func viewLen(s string) int {
	for _, m := range ansi.FindAllString(s, -1) {
		s = strings.Replace(s, m, "", -1)
	}
	return utf8.RuneCountInString(s)
}

func maxLen(strings []string) int {
	maxLen := 0
	for _, s := range strings {
		len := viewLen(s)
		if len > maxLen {
			maxLen = len
		}
	}
	return maxLen
}

type alignment int

const (
	alignLeft   = alignment(0)
	alignCenter = alignment(1)
	alignRight  = alignment(2)
)

func getPads(s string, maxLen int, align alignment) (lPad int, rPad int) {
	len := viewLen(s)
	diff := maxLen - len

	if align == alignLeft {
		lPad = 0
		rPad = diff - lPad + 1
	} else if align == alignCenter {
		lPad = diff / 2
		rPad = diff - lPad + 1
	} /* else {
		TODO
	} */

	return
}

func padded(s string, maxLen int, align alignment) string {
	lPad, rPad := getPads(s, maxLen, align)
	return fmt.Sprintf("%s%s%s", strings.Repeat(" ", lPad), s, strings.Repeat(" ", rPad))
}

func lineSeparator(num int, columns []string, rows [][]string) string {
	lineSep := ""
	first := ""
	div := ""
	end := ""

	if num == 0 {
		first = "┌"
		div = "┬"
		end = "┐"
	} else if num == 1 {
		first = "├"
		div = "┼"
		end = "┤"
	} else if num == 2 {
		first = "└"
		div = "┴"
		end = "┘"
	}

	for colIndex, colHeader := range columns {
		column := []string{colHeader}
		for _, row := range rows {
			column = append(column, row[colIndex])
		}
		mLen := maxLen(column)
		if colIndex == 0 {
			lineSep += fmt.Sprintf(first+"%s", strings.Repeat("─", mLen+1))
		} else {
			lineSep += fmt.Sprintf(div+"%s", strings.Repeat("─", mLen+1))
		}
	}
	lineSep += end

	return lineSep
}

// Table accepts a slice of column labels and a 2d slice of rows
// and prints on the writer an ASCII based datagrid of such
// data.
func Table(w io.Writer, columns []string, rows [][]string) {
	headers := make([]string, len(columns))
	for i, col := range columns {
		headers[i] = fmt.Sprintf(" %s", col)
	}

	cells := make([][]string, len(rows))
	for i, row := range rows {
		cells[i] = make([]string, len(row))
		for j, cell := range row {
			cells[i][j] = fmt.Sprintf(" %s", cell)
		}
	}

	colPaddings := make([]int, 0)
	for colIndex, colHeader := range headers {
		column := []string{colHeader}
		for _, row := range cells {
			column = append(column, row[colIndex])
		}
		mLen := maxLen(column)
		colPaddings = append(colPaddings, mLen)
	}

	table := "\n"

	// header
	table += fmt.Sprintf("%s\n", lineSeparator(0, headers, cells))
	for colIndex, colHeader := range headers {
		table += fmt.Sprintf("│%s", padded(colHeader, colPaddings[colIndex], alignCenter))
	}
	table += fmt.Sprintf("│\n")

	table += fmt.Sprintf("%s\n", lineSeparator(1, headers, cells))

	// rows
	for _, row := range cells {
		for colIndex, cell := range row {
			table += fmt.Sprintf("│%s", padded(cell, colPaddings[colIndex], alignLeft))
		}
		table += fmt.Sprintf("│\n")
	}

	// footer
	table += fmt.Sprintf("%s\n", lineSeparator(2, headers, cells))

	fmt.Fprintf(w, "%s", table)
}