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

Commit

Permalink
Merge pull request #3 from InVisionApp/select-star
Browse files Browse the repository at this point in the history
supports select * style of table parsing and printing
  • Loading branch information
vmogilev authored May 14, 2018
2 parents 7afe17a 5772e53 commit b72a1a0
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 113 deletions.
59 changes: 22 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 24,17 @@ import (
"github.com/InVisionApp/tabular"
)

var tab tabular.Columns
var tab tabular.Table

func init() {
tab = tabular.New()
tab.Col("env", "Environment", 14)
tab.Col("cls", "Cluster", 10)
tab.Col("svc", "Service", 15)
tab.Col("hst", "Database Host", 20)
tab.Col("pct", "%CPU", 7)
tab["pct"].RightJustified = true
tab.ColRJ("pct", "%CPU", 7)
}

// Sample data-set
var data = []struct {
e, c, s, d string
v float64
Expand Down Expand Up @@ -72,30 70,24 @@ var data = []struct {
}

func main() {
// Print Environments and Clusters
// Print a subset of columns (Environments and Clusters)
format := tab.Print("env", "cls")
for _, x := range data {
fmt.Printf(format, x.e, x.c)
}

// Print Environments, Clusters and Services
format = tab.Print("env", "cls", "svc")
// Print All Columns
format = tab.Print("*")
for _, x := range data {
fmt.Printf(format, x.e, x.c, x.s)
fmt.Printf(format, x.e, x.c, x.s, x.d, x.v)
}

// Print Everything
format = tab.Print("cls", "svc", "hst", "pct")
for _, x := range data {
fmt.Printf(format, x.c, x.s, x.d, x.v)
}

// Print Everything to a custom destination such as a log
table := tab.Parse("cls", "svc", "hst", "pct")
// Print All Columns to a custom destination such as a log
table := tab.Parse("*")
log.Println(table.Header)
log.Println(table.SubHeader)
for _, x := range data {
log.Printf(table.Format, x.c, x.s, x.d, x.v)
log.Printf(table.Format, x.e, x.c, x.s, x.d, x.v)
}
}
```
Expand All @@ -110,24 102,17 @@ production cluster-1
production cluster-2
production cluster-2
Environment Cluster Service
-------------- ---------- ---------------
production cluster-1 service-a
production cluster-1 service-b
production cluster-2 service-a
production cluster-2 service-b
Cluster Service Database Host %CPU
---------- --------------- -------------------- -------
cluster-1 service-a database-host-1 70.01
cluster-1 service-b database-host-2 99.51
cluster-2 service-a database-host-1 70.01
cluster-2 service-b database-host-2 99.51
2018/04/26 10:17:27 Cluster Service Database Host %CPU
2018/04/26 10:17:27 ---------- --------------- -------------------- -------
2018/04/26 10:17:27 cluster-1 service-a database-host-1 70.01
2018/04/26 10:17:27 cluster-1 service-b database-host-2 99.51
2018/04/26 10:17:27 cluster-2 service-a database-host-1 70.01
2018/04/26 10:17:27 cluster-2 service-b database-host-2 99.51
Environment Cluster Service Database Host %CPU
-------------- ---------- --------------- -------------------- -------
production cluster-1 service-a database-host-1 70.01
production cluster-1 service-b database-host-2 99.51
production cluster-2 service-a database-host-1 70.01
production cluster-2 service-b database-host-2 99.51
2018/05/14 11:19:41 Environment Cluster Service Database Host %CPU
2018/05/14 11:19:41 -------------- ---------- --------------- -------------------- -------
2018/05/14 11:19:41 production cluster-1 service-a database-host-1 70.01
2018/05/14 11:19:41 production cluster-1 service-b database-host-2 99.51
2018/05/14 11:19:41 production cluster-2 service-a database-host-1 70.01
2018/05/14 11:19:41 production cluster-2 service-b database-host-2 99.51
```
25 changes: 9 additions & 16 deletions example/example.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 7,15 @@ import (
"github.com/InVisionApp/tabular"
)

var tab tabular.Columns
var tab tabular.Table

func init() {
tab = tabular.New()
tab.Col("env", "Environment", 14)
tab.Col("cls", "Cluster", 10)
tab.Col("svc", "Service", 15)
tab.Col("hst", "Database Host", 20)
tab.Col("pct", "%CPU", 7)
tab["pct"].RightJustified = true
tab.ColRJ("pct", "%CPU", 7)
}

var data = []struct {
Expand Down Expand Up @@ -54,29 53,23 @@ var data = []struct {
}

func main() {
// Print Environments and Clusters
// Print a subset of columns (Environments and Clusters)
format := tab.Print("env", "cls")
for _, x := range data {
fmt.Printf(format, x.e, x.c)
}

// Print Environments, Clusters and Services
format = tab.Print("env", "cls", "svc")
// Print All Columns
format = tab.Print("*")
for _, x := range data {
fmt.Printf(format, x.e, x.c, x.s)
fmt.Printf(format, x.e, x.c, x.s, x.d, x.v)
}

// Print Clusters, Services and Database Hosts
format = tab.Print("cls", "svc", "hst", "pct")
for _, x := range data {
fmt.Printf(format, x.c, x.s, x.d, x.v)
}

// Print to a custom destination such as a log
table := tab.Parse("cls", "svc", "hst", "pct")
// Print All Columns to a custom destination such as a log
table := tab.Parse("*")
log.Println(table.Header)
log.Println(table.SubHeader)
for _, x := range data {
log.Printf(table.Format, x.c, x.s, x.d, x.v)
log.Printf(table.Format, x.e, x.c, x.s, x.d, x.v)
}
}
28 changes: 16 additions & 12 deletions format_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 8,41 @@ import (

func TestFormat(t *testing.T) {
tab := tabular.New()
tab.Col("id", "ID", 6)
tab.ColRJ("id", "ID", 6)
tab.Col("env", "Environment", 14)
tab.Col("cls", "Cluster", 10)
tab.Col("svc", "Service", 25)
tab.Col("hst", "Database Host", 25)
tab.Col("pct", "%CPU", 5)
tab["id"].RightJustified = true
tab["pct"].RightJustified = true
tab.ColRJ("pct", "%CPU", 5)

tWant := tabular.Table{
// Test Partial Printing
want := "%6v %-14v %-10v\n"
if got := tab.Print("id", "env", "cls"); got != want {
t.Errorf("ERROR: tab.Print() failed\n want: %q\n got: %q", want, got)
}

tWant := tabular.Output{
Header: " ID Environment Cluster Service Database Host %CPU",
SubHeader: "------ -------------- ---------- ------------------------- ------------------------- -----",
Format: "%6v %-14v %-10v %-25v %-25v %5v\n",
}

// Test Printing
want := tWant.Format
if got := tab.Print("id", "env", "cls", "svc", "hst", "pct"); got != want {
t.Errorf("ERROR: tab.Print() failed\n want: %q\n got: %q", want, got)
// Test Printing All
want = tWant.Format
if got := tab.Print(tabular.All); got != want {
t.Errorf("ERROR: tab.Print(All) failed\n want: %q\n got: %q", want, got)
}

// Test Parsing
if tGot := tab.Parse("id", "env", "cls", "svc", "hst", "pct"); tGot != tWant {
if tGot.Header != tWant.Header {
t.Errorf("ERROR: tab.Parse() failed\n want: %v\n got: %v", tWant.Header, tGot.Header)
t.Errorf("ERROR: tab.Parse() failed\n want: %q\n got: %q", tWant.Header, tGot.Header)
}
if tGot.SubHeader != tWant.SubHeader {
t.Errorf("ERROR: tab.Parse() failed\n want: %v\n got: %v", tWant.SubHeader, tGot.SubHeader)
t.Errorf("ERROR: tab.Parse() failed\n want: %q\n got: %q", tWant.SubHeader, tGot.SubHeader)
}
if tGot.Format != tWant.Format {
t.Errorf("ERROR: tab.Parse() failed\n want: %v\n got: %v", tWant.Format, tGot.Format)
t.Errorf("ERROR: tab.Parse() failed\n want: %q\n got: %q", tWant.Format, tGot.Format)
}
}
}
88 changes: 54 additions & 34 deletions tabular.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 5,19 @@ import (
"strings"
)

// Table - parsed table's header, subheader and format specifier
type Table struct {
// Output - parsed table's header, subheader and format specifier
type Output struct {
Header string
SubHeader string
Format string
}

// Columns - maps short names of columns to their structure defining:
// full name, length and whether it's right justified
//
// For Example:
// "env": Column{Name: "Environment", Length: 14},
// "cls": Column{Name: "Cluster", Length: 40},
// "srv": Column{Name: "Service", Length: 35},
// "hst": Column{Name: "Host", Length: 45},
// "pct": Column{Name: "%CPU", Length: 7, RightJustified: true},
type Columns map[string]*Column
// Table - maps and orders short names of columns to their structure defining:
// full name, length and whether it's right justified
type Table struct {
Columns map[string]*Column
order *[]string
}

// Column - defines column's name, length and if it's right justified
type Column struct {
Expand All @@ -30,64 26,88 @@ type Column struct {
RightJustified bool
}

// New - Creates a map of tabular Columns
func New() Columns { return Columns{} }
// All - pass this to Print() or Parse() to print or parse all columns of a table
const All = "*"

// New - Creates a new table
func New() Table {
return Table{
Columns: map[string]*Column{},
order: &[]string{},
}
}

// Print - does the following:
//
// 1) prints a table style heading for a given list of columns.
//
// For example if Columns are defined as:
// 1) prints a table style heading for a given list of columns,
// for example, if Columns are defined as:
//
// "env": Column{Name: "Environment", Length: 14},
// "cls": Column{Name: "Cluster", Length: 40},
// "srv": Column{Name: "Service", Length: 35},
//
// It'll produce:
// It'll produce:
//
// Environment Cluster Service
// -------------- ---------------------------------------- -----------------------------------
//
// 2) Returns an fmt style format specifier string that you can use
// to output values under the above heading via Printf(format,...):
// to output values under the above heading via Printf(format,...):
//
// %-14v %-40v %-35v
func (cl Columns) Print(cols ...string) string {
t := cl.parse(cols...)
func (tbl Table) Print(cols ...string) string {
t := tbl.parse(cols...)
fmt.Println(t.Header)
fmt.Println(t.SubHeader)
return t.Format
}

// Parse - builds a Table out of a given list of columns
// Parse - constructs Table's Output structure containing it's header,
// sub-header and format modifier out of a given list of columns.
//
// To simply print the table's title call Print() instead
// To simply print the table's title call Print() instead.
//
// Parse() is usefull when you need to control where
// to output the title, for example to a log or a trace file
func (cl Columns) Parse(cols ...string) Table {
return cl.parse(cols...)
// to output the title, for example to a log or a trace file.
func (tbl Table) Parse(cols ...string) Output {
return tbl.parse(cols...)
}

// Col - adds a new column to existing tabular Format
func (cl Columns) Col(shortName, fullName string, columnLength int) {
cl[shortName] = &Column{Name: fullName, Length: columnLength}
// Col - adds a new column to an existing table
func (tbl Table) Col(shortName, fullName string, columnLength int) {
tbl.Columns[shortName] = &Column{Name: fullName, Length: columnLength}
tbl.appendColumn(shortName)
}

func (cl Columns) parse(cols ...string) Table {
// ColRJ - adds a new Right Justified column to an existing table
func (tbl Table) ColRJ(shortName, fullName string, columnLength int) {
tbl.Columns[shortName] = &Column{Name: fullName, Length: columnLength, RightJustified: true}
tbl.appendColumn(shortName)
}

func (tbl Table) appendColumn(shortName string) {
*tbl.order = append(*tbl.order, shortName)
}

func (tbl Table) parse(cols ...string) Output {
var header string
var subHeader string
var format string
var space string

if len(cols) == 1 && cols[0] == All {
cols = *tbl.order
}

for _, c := range cols {
cf := cl[c].f()
header = header space fmt.Sprintf(cf, cl[c].Name)
subHeader = subHeader space fmt.Sprintf(cf, r(cl[c].Length))
cf := tbl.Columns[c].f()
header = header space fmt.Sprintf(cf, tbl.Columns[c].Name)
subHeader = subHeader space fmt.Sprintf(cf, r(tbl.Columns[c].Length))
format = format space cf
space = " "
}

return Table{
return Output{
Header: header,
SubHeader: subHeader,
Format: format "\n",
Expand Down
Loading

0 comments on commit b72a1a0

Please sign in to comment.