Skip to content
This repository was archived by the owner on Dec 23, 2024. It is now read-only.

Commit 8679916

Browse files
authored
Merge pull request #1 from InVisionApp/siplify-api
reworked public API and added Parse to support other output destinations
2 parents c661599 + bf5ba28 commit 8679916

File tree

6 files changed

+187
-44
lines changed

6 files changed

+187
-44
lines changed

.travis.yml

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ language: go
22

33
script:
44
- go test -race -coverprofile=coverage.txt -covermode=atomic
5+
- go run example/example.go
56

67
after_success:
78
- bash <(curl -s https://codecov.io/bash)

README.md

+33-12
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,37 @@
66

77
# tabular
88

9-
Tabular package to print ASCII tables from command line utilities.
9+
Tabular simplifies printing ASCII tables from command line utilities without the need to pass large sets of data to it's API.
1010

11-
Example:
11+
Simply define the table columns and `tabular` will parse the right [format specifier](https://golang.org/pkg/fmt/#Printf) that you can use in your calls to `fmt` or any other function that supports it.
12+
13+
Table columns can be defined once and then reused over and over again making it easy to modify column length and heading in one place. And a subset of columns can be specified during Print() or Parse() calls to modify the table's title without redefining it.
14+
15+
Example (also available in [`example/example.go`](example/example.go):
1216

1317
```go
1418
package main
1519

1620
import (
1721
"fmt"
22+
"log"
1823

1924
"github.com/InVisionApp/tabular"
2025
)
2126

22-
var tab tabular.Format
27+
var tab tabular.Columns
2328

2429
func init() {
2530
tab = tabular.New()
26-
tab.Add("env", "Environment", 14)
27-
tab.Add("cls", "Cluster", 10)
28-
tab.Add("svc", "Service", 15)
29-
tab.Add("hst", "Database Host", 20)
30-
tab.Add("pct", "%CPU", 7)
31+
tab.Col("env", "Environment", 14)
32+
tab.Col("cls", "Cluster", 10)
33+
tab.Col("svc", "Service", 15)
34+
tab.Col("hst", "Database Host", 20)
35+
tab.Col("pct", "%CPU", 7)
3136
tab["pct"].RightJustified = true
3237
}
3338

39+
// Sample data-set
3440
var data = []struct {
3541
e, c, s, d string
3642
v float64
@@ -67,22 +73,30 @@ var data = []struct {
6773

6874
func main() {
6975
// Print Environments and Clusters
70-
format := tab.Do("env", "cls")
76+
format := tab.Print("env", "cls")
7177
for _, x := range data {
7278
fmt.Printf(format, x.e, x.c)
7379
}
7480

7581
// Print Environments, Clusters and Services
76-
format = tab.Do("env", "cls", "svc")
82+
format = tab.Print("env", "cls", "svc")
7783
for _, x := range data {
7884
fmt.Printf(format, x.e, x.c, x.s)
7985
}
8086

81-
// Print Clusters, Services and Database Hosts
82-
format = tab.Do("cls", "svc", "hst", "pct")
87+
// Print Everything
88+
format = tab.Print("cls", "svc", "hst", "pct")
8389
for _, x := range data {
8490
fmt.Printf(format, x.c, x.s, x.d, x.v)
8591
}
92+
93+
// Print Everything to a custom destination such as a log
94+
table := tab.Parse("cls", "svc", "hst", "pct")
95+
log.Println(table.Header)
96+
log.Println(table.SubHeader)
97+
for _, x := range data {
98+
log.Printf(table.Format, x.c, x.s, x.d, x.v)
99+
}
86100
}
87101
```
88102

@@ -109,4 +123,11 @@ cluster-1 service-a database-host-1 70.01
109123
cluster-1 service-b database-host-2 99.51
110124
cluster-2 service-a database-host-1 70.01
111125
cluster-2 service-b database-host-2 99.51
126+
127+
2018/04/26 10:17:27 Cluster Service Database Host %CPU
128+
2018/04/26 10:17:27 ---------- --------------- -------------------- -------
129+
2018/04/26 10:17:27 cluster-1 service-a database-host-1 70.01
130+
2018/04/26 10:17:27 cluster-1 service-b database-host-2 99.51
131+
2018/04/26 10:17:27 cluster-2 service-a database-host-1 70.01
132+
2018/04/26 10:17:27 cluster-2 service-b database-host-2 99.51
112133
```

example/example.go

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
7+
"github.com/InVisionApp/tabular"
8+
)
9+
10+
var tab tabular.Columns
11+
12+
func init() {
13+
tab = tabular.New()
14+
tab.Col("env", "Environment", 14)
15+
tab.Col("cls", "Cluster", 10)
16+
tab.Col("svc", "Service", 15)
17+
tab.Col("hst", "Database Host", 20)
18+
tab.Col("pct", "%CPU", 7)
19+
tab["pct"].RightJustified = true
20+
}
21+
22+
var data = []struct {
23+
e, c, s, d string
24+
v float64
25+
}{
26+
{
27+
e: "production",
28+
c: "cluster-1",
29+
s: "service-a",
30+
d: "database-host-1",
31+
v: 70.01,
32+
},
33+
{
34+
e: "production",
35+
c: "cluster-1",
36+
s: "service-b",
37+
d: "database-host-2",
38+
v: 99.51,
39+
},
40+
{
41+
e: "production",
42+
c: "cluster-2",
43+
s: "service-a",
44+
d: "database-host-1",
45+
v: 70.01,
46+
},
47+
{
48+
e: "production",
49+
c: "cluster-2",
50+
s: "service-b",
51+
d: "database-host-2",
52+
v: 99.51,
53+
},
54+
}
55+
56+
func main() {
57+
// Print Environments and Clusters
58+
format := tab.Print("env", "cls")
59+
for _, x := range data {
60+
fmt.Printf(format, x.e, x.c)
61+
}
62+
63+
// Print Environments, Clusters and Services
64+
format = tab.Print("env", "cls", "svc")
65+
for _, x := range data {
66+
fmt.Printf(format, x.e, x.c, x.s)
67+
}
68+
69+
// Print Clusters, Services and Database Hosts
70+
format = tab.Print("cls", "svc", "hst", "pct")
71+
for _, x := range data {
72+
fmt.Printf(format, x.c, x.s, x.d, x.v)
73+
}
74+
75+
// Print to a custom destination such as a log
76+
table := tab.Parse("cls", "svc", "hst", "pct")
77+
log.Println(table.Header)
78+
log.Println(table.SubHeader)
79+
for _, x := range data {
80+
log.Printf(table.Format, x.c, x.s, x.d, x.v)
81+
}
82+
}

format_test.go

+20-8
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,27 @@ import (
88

99
func TestFormat(t *testing.T) {
1010
tab := tabular.New()
11-
tab.Add("env", "Environment", 14)
12-
tab.Add("cls", "Cluster", 10)
13-
tab.Add("svc", "Service", 25)
14-
tab.Add("hst", "Database Host", 25)
15-
tab.Add("pct", "%CPU", 5)
11+
tab.Col("env", "Environment", 14)
12+
tab.Col("cls", "Cluster", 10)
13+
tab.Col("svc", "Service", 25)
14+
tab.Col("hst", "Database Host", 25)
15+
tab.Col("pct", "%CPU", 5)
1616
tab["pct"].RightJustified = true
1717

18-
want := "%-14v %-10v %-25v %-25v %5v\n"
19-
if got := tab.Do("env", "cls", "svc", "hst", "pct"); got != want {
20-
t.Fatalf("ERROR: tab.Do() failed\n want: %q\n got: %q", want, got)
18+
tWant := tabular.Table{
19+
Header: "Environment Cluster Service Database Host %CPU",
20+
SubHeader: "-------------- ---------- ------------------------- ------------------------- -----",
21+
Format: "%-14v %-10v %-25v %-25v %5v\n",
22+
}
23+
24+
// Test Printing
25+
want := tWant.Format
26+
if got := tab.Print("env", "cls", "svc", "hst", "pct"); got != want {
27+
t.Errorf("ERROR: tab.Print() failed\n want: %q\n got: %q", want, got)
28+
}
29+
30+
// Test Parsing
31+
if tGot := tab.Parse("env", "cls", "svc", "hst", "pct"); tGot != tWant {
32+
t.Errorf("ERROR: tab.Parse() failed\n want: %v\n got: %v", tWant, tGot)
2133
}
2234
}

tabular.go

+49-22
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ import (
55
"strings"
66
)
77

8-
// Format - maps short names of columns to their structure defining:
8+
// Table - parsed table's header, subheader and format specifier
9+
type Table struct {
10+
Header string
11+
SubHeader string
12+
Format string
13+
}
14+
15+
// Columns - maps short names of columns to their structure defining:
916
// full name, length and whether it's right justified
1017
//
1118
// For Example:
@@ -14,7 +21,7 @@ import (
1421
// "srv": Column{Name: "Service", Length: 35},
1522
// "hst": Column{Name: "Host", Length: 45},
1623
// "pct": Column{Name: "%CPU", Length: 7, RightJustified: true},
17-
type Format map[string]*Column
24+
type Columns map[string]*Column
1825

1926
// Column - defines column's name, length and if it's right justified
2027
type Column struct {
@@ -23,11 +30,14 @@ type Column struct {
2330
RightJustified bool
2431
}
2532

26-
// Do - does the following:
33+
// New - Creates a map of tabular Columns
34+
func New() Columns { return Columns{} }
35+
36+
// Print - does the following:
2737
//
2838
// 1) prints a table style heading for a given list of columns.
2939
//
30-
// For example if Format is defined as:
40+
// For example if Columns are defined as:
3141
//
3242
// "env": Column{Name: "Environment", Length: 14},
3343
// "cls": Column{Name: "Cluster", Length: 40},
@@ -38,30 +48,47 @@ type Column struct {
3848
// Environment Cluster Service
3949
// -------------- ---------------------------------------- -----------------------------------
4050
//
41-
// 2) Returns an fmt style `format` string to output values
42-
// under the above heading via Printf(format,...):
51+
// 2) Returns an fmt style format specifier string that you can use
52+
// to output values under the above heading via Printf(format,...):
4353
//
4454
// %-14v %-40v %-35v
45-
func (fm Format) Do(cols ...string) string {
46-
var title string
47-
var uline string
55+
func (cl Columns) Print(cols ...string) string {
56+
t := cl.parse(cols...)
57+
fmt.Println(t.Header)
58+
fmt.Println(t.SubHeader)
59+
return t.Format
60+
}
61+
62+
// Parse - builds a Table out of a given list of columns
63+
//
64+
// To simply print the table's title call Print() instead
65+
//
66+
// Parse() is usefull when you need to control where
67+
// to output the title, for example to a log or a trace file
68+
func (cl Columns) Parse(cols ...string) Table {
69+
return cl.parse(cols...)
70+
}
71+
72+
// Col - adds a new column to existing tabular Format
73+
func (cl Columns) Col(shortName, fullName string, columnLength int) {
74+
cl[shortName] = &Column{Name: fullName, Length: columnLength}
75+
}
76+
77+
func (cl Columns) parse(cols ...string) Table {
78+
var header string
79+
var subHeader string
4880
var format string
4981
for _, c := range cols {
50-
title = title + " " + fmt.Sprintf(fm[c].f(), fm[c].Name)
51-
uline = uline + " " + fmt.Sprintf(fm[c].f(), r(fm[c].Length))
52-
format = format + " " + fm[c].f()
82+
header = header + " " + fmt.Sprintf(cl[c].f(), cl[c].Name)
83+
subHeader = subHeader + " " + fmt.Sprintf(cl[c].f(), r(cl[c].Length))
84+
format = format + " " + cl[c].f()
5385
}
54-
fmt.Println(strings.TrimSpace(title))
55-
fmt.Println(strings.TrimSpace(uline))
56-
return strings.TrimSpace(format) + "\n"
57-
}
58-
59-
// New - Creates a new tabular Format
60-
func New() Format { return Format{} }
6186

62-
// Add - adds a new column to existing tabular Format
63-
func (fm Format) Add(shortName, fullName string, columnLength int) {
64-
fm[shortName] = &Column{Name: fullName, Length: columnLength}
87+
return Table{
88+
Header: strings.TrimSpace(header),
89+
SubHeader: strings.TrimSpace(subHeader),
90+
Format: strings.TrimSpace(format) + "\n",
91+
}
6592
}
6693

6794
// f() returns fmt formatting, for example:

tabular_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
)
77

88
func TestTabular(t *testing.T) {
9-
want := Format{
9+
want := Columns{
1010
"env": &Column{Name: "Environment", Length: 14},
1111
"cls": &Column{Name: "Cluster", Length: 10},
1212
"svc": &Column{Name: "Service", Length: 25},
@@ -16,7 +16,7 @@ func TestTabular(t *testing.T) {
1616

1717
got := New()
1818
for k, v := range want {
19-
got.Add(k, v.Name, v.Length)
19+
got.Col(k, v.Name, v.Length)
2020
got[k].RightJustified = v.RightJustified
2121
}
2222

0 commit comments

Comments
 (0)