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

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: InVisionApp/tabular
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.1.0
Choose a base ref
...
head repository: InVisionApp/tabular
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
  • 8 commits
  • 6 files changed
  • 3 contributors

Commits on Apr 26, 2018

  1. readme updates

    vmogilev committed Apr 26, 2018

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    4d2b8bc View commit details

Commits on May 10, 2018

  1. Copy the full SHA
    9d7aa76 View commit details
  2. Merge pull request #2 from InVisionApp/heading-fix

    fixes header padding error when first column is RightJustified
    vmogilev authored May 10, 2018
    Copy the full SHA
    7afe17a View commit details

Commits on May 14, 2018

  1. Copy the full SHA
    5772e53 View commit details
  2. Merge pull request #3 from InVisionApp/select-star

    supports select * style of table parsing and printing
    vmogilev authored May 14, 2018
    Copy the full SHA
    b72a1a0 View commit details

Commits on May 12, 2023

  1. GH teams audit

    bstanley0811 committed May 12, 2023
    Copy the full SHA
    b82b48e View commit details

Commits on Dec 23, 2024

  1. Sunset

    maxcnunes authored Dec 23, 2024
    Copy the full SHA
    0b05f50 View commit details
  2. Merge pull request #4 from InVisionApp/sunset

    Sunset
    maxcnunes authored Dec 23, 2024
    Copy the full SHA
    ffa2583 View commit details
Showing with 160 additions and 120 deletions.
  1. +1 −0 CODEOWNERS
  2. +29 −41 README.md
  3. +9 −16 example/example.go
  4. +26 −12 format_test.go
  5. +60 −37 tabular.go
  6. +35 −14 tabular_test.go
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @InVisionApp/platform
70 changes: 29 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
| :warning: This project is no longer actively supported.
| ---

[![LICENSE](https://img.shields.io/badge/license-MIT-orange.svg)](LICENSE)
[![Build Status](https://travis-ci.org/InVisionApp/tabular.svg?branch=master)](https://travis-ci.org/InVisionApp/tabular)
[![codecov](https://codecov.io/gh/InVisionApp/tabular/branch/master/graph/badge.svg)](https://codecov.io/gh/InVisionApp/tabular)
@@ -8,11 +11,11 @@

Tabular simplifies printing ASCII tables from command line utilities without the need to pass large sets of data to it's API.

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.
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.Printf()` or any other function that supports it.

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.
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 `tabular.Print()` or `tabular.Parse()` calls to modify the table's title without redefining it.

Example (also available in [`example/example.go`](example/example.go):
Example (also available in [`example/example.go`](example/example.go)):

```go
package main
@@ -24,19 +27,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
@@ -72,30 +73,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)
}
}
```
@@ -110,24 +105,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
@@ -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 {
@@ -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)
}
}
38 changes: 26 additions & 12 deletions format_test.go
Original file line number Diff line number Diff line change
@@ -8,27 +8,41 @@ import (

func TestFormat(t *testing.T) {
tab := tabular.New()
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["pct"].RightJustified = true
tab.ColRJ("pct", "%CPU", 5)

tWant := tabular.Table{
Header: "Environment Cluster Service Database Host %CPU",
SubHeader: "-------------- ---------- ------------------------- ------------------------- -----",
Format: "%-14v %-10v %-25v %-25v %5v\n",
// 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)
}

// Test Printing
want := tWant.Format
if got := tab.Print("env", "cls", "svc", "hst", "pct"); 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 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("env", "cls", "svc", "hst", "pct"); tGot != tWant {
t.Errorf("ERROR: tab.Parse() failed\n want: %v\n got: %v", tWant, tGot)
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: %q\n got: %q", tWant.Header, tGot.Header)
}
if tGot.SubHeader != tWant.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: %q\n got: %q", tWant.Format, tGot.Format)
}
}
}
97 changes: 60 additions & 37 deletions tabular.go
Original file line number Diff line number Diff line change
@@ -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 {
@@ -30,64 +26,91 @@ 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 {
header = header + " " + fmt.Sprintf(cl[c].f(), cl[c].Name)
subHeader = subHeader + " " + fmt.Sprintf(cl[c].f(), r(cl[c].Length))
format = format + " " + cl[c].f()
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{
Header: strings.TrimSpace(header),
SubHeader: strings.TrimSpace(subHeader),
Format: strings.TrimSpace(format) + "\n",
return Output{
Header: header,
SubHeader: subHeader,
Format: format + "\n",
}
}

Loading