refactored output printer, now using tablewriter

This commit is contained in:
2022-10-01 14:14:53 +02:00
parent 1b1b63caa3
commit 4ca3a56280
9 changed files with 203 additions and 70 deletions

View File

@@ -33,6 +33,13 @@ var rootCmd = &cobra.Command{
return nil
}
lib.PrepareColumns()
err := lib.PrepareModeFlags()
if err != nil {
return err
}
return lib.ProcessFiles(args)
},
}
@@ -46,9 +53,17 @@ func Execute() {
func init() {
rootCmd.PersistentFlags().BoolVarP(&lib.Debug, "debug", "d", false, "Enable debugging")
rootCmd.PersistentFlags().BoolVarP(&lib.XtendedOut, "extended", "x", false, "Enable extended output")
rootCmd.PersistentFlags().BoolVarP(&lib.NoNumbering, "no-numbering", "n", false, "Disable header numbering")
rootCmd.PersistentFlags().BoolVarP(&lib.ShowVersion, "version", "v", false, "Print program version")
rootCmd.PersistentFlags().StringVarP(&lib.Separator, "separator", "s", "", "Custom field separator")
rootCmd.PersistentFlags().StringVarP(&lib.Columns, "columns", "c", "", "Only show the speficied columns (separated by ,)")
// output flags, only 1 allowed
rootCmd.PersistentFlags().BoolVarP(&lib.OutflagExtended, "extended", "X", false, "Enable extended output")
rootCmd.PersistentFlags().BoolVarP(&lib.OutflagMarkdown, "markdown", "M", false, "Enable markdown table output")
rootCmd.PersistentFlags().BoolVarP(&lib.OutflagOrgtable, "orgtbl", "O", false, "Enable org-mode table output")
rootCmd.MarkFlagsMutuallyExclusive("extended", "markdown", "orgtbl")
// same thing but more common, takes precedence over above group
rootCmd.PersistentFlags().StringVarP(&lib.OutputMode, "output", "o", "", "Output mode - one of: orgtbl, markdown, extended, ascii(default)")
}

2
go.mod
View File

@@ -4,11 +4,13 @@ go 1.18
require (
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897
github.com/olekukonko/tablewriter v0.0.5
github.com/spf13/cobra v1.5.0
)
require (
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.8.0 // indirect
)

4
go.sum
View File

@@ -6,6 +6,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=

View File

@@ -17,6 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package lib
// command line flags
var Debug bool
var XtendedOut bool
var NoNumbering bool
@@ -24,5 +25,10 @@ var ShowVersion bool
var Columns string
var UseColumns []int
var Separator string
var OutflagExtended bool
var OutflagMarkdown bool
var OutflagOrgtable bool
var OutputMode string
var Version = "v1.0.2"
var validOutputmodes = "(orgtbl|markdown|extended|ascii)"

View File

@@ -18,8 +18,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package lib
import (
"errors"
"fmt"
"os"
"regexp"
"strconv"
"strings"
)
@@ -38,7 +40,7 @@ func contains(s []int, e int) bool {
return false
}
func prepareColumns() {
func PrepareColumns() {
if len(Columns) > 0 {
for _, use := range strings.Split(Columns, ",") {
usenum, err := strconv.Atoi(use)
@@ -49,3 +51,32 @@ func prepareColumns() {
}
}
}
func PrepareModeFlags() error {
if len(OutputMode) == 0 {
switch {
case OutflagExtended:
OutputMode = "extended"
case OutflagMarkdown:
OutputMode = "markdown"
case OutflagOrgtable:
OutputMode = "orgtbl"
default:
OutputMode = "ascii"
}
} else {
r, err := regexp.Compile(validOutputmodes)
if err != nil {
return errors.New("Failed to validate output mode spec!")
}
match := r.MatchString(OutputMode)
if !match {
return errors.New("Invalid output mode!")
}
}
return nil
}

View File

@@ -27,7 +27,7 @@ func ProcessFiles(args []string) error {
var pattern string
havefiles := false
prepareColumns()
//prepareColumns()
if len(args) > 0 {
if _, err := os.Stat(args[0]); err != nil {

View File

@@ -144,7 +144,7 @@ func parseFile(input io.Reader, pattern string) Tabdata {
// if Debug {
// fmt.Printf("<%s> ", value)
// }
values = append(values, value)
values = append(values, strings.TrimSpace(value))
idx++
}

View File

@@ -19,80 +19,148 @@ package lib
import (
"fmt"
"github.com/olekukonko/tablewriter"
"os"
"regexp"
"strings"
)
func printData(data Tabdata) {
if XtendedOut {
printExtendedData(data)
} else {
printTabularData(data)
}
}
func printTabularData(data Tabdata) {
// needed for data output
var formats []string
if len(data.entries) > 0 {
// headers
// prepare headers
// FIXME: maybe do this already in parseFile()?
if !NoNumbering {
numberedHeaders := []string{}
for i, head := range data.headers {
if len(Columns) > 0 {
if !contains(UseColumns, i+1) {
continue
}
}
// calculate column width
var width int
var iwidth int
var format string
// generate format string
if len(head) > data.maxwidthPerCol[i] {
width = len(head)
} else {
width = data.maxwidthPerCol[i]
numberedHeaders = append(numberedHeaders, fmt.Sprintf("%s(%d)", head, i+1))
}
data.headers = numberedHeaders
}
if NoNumbering {
iwidth = 0
} else {
iwidth = len(fmt.Sprintf("%d", i)) // in case i > 9
}
format = fmt.Sprintf("%%-%ds", 3+iwidth+width)
if NoNumbering {
fmt.Printf(format, fmt.Sprintf("%s ", head))
} else {
fmt.Printf(format, fmt.Sprintf("%s(%d) ", head, i+1))
}
// register
formats = append(formats, format)
}
fmt.Println()
// entries
var idx int
for _, entry := range data.entries {
idx = 0
//fmt.Println(entry)
for i, value := range entry {
// prepare data
if len(Columns) > 0 {
reducedEntries := [][]string{}
reducedEntry := []string{}
for _, entry := range data.entries {
reducedEntry = nil
for i, value := range entry {
if !contains(UseColumns, i+1) {
continue
}
reducedEntry = append(reducedEntry, value)
}
fmt.Printf(formats[idx], strings.TrimSpace(value))
idx++
reducedEntries = append(reducedEntries, reducedEntry)
}
fmt.Println()
data.entries = reducedEntries
}
switch OutputMode {
case "extended":
printExtendedData(data)
case "ascii":
printAsciiData(data)
case "orgtbl":
printOrgmodeData(data)
case "markdown":
printMarkdownData(data)
default:
printAsciiData(data)
}
}
func trimRow(row []string) []string {
// FIXME: remove this when we only use Tablewriter and strip in ParseFile()!
var fixedrow []string
for _, cell := range row {
fixedrow = append(fixedrow, strings.TrimSpace(cell))
}
return fixedrow
}
/*
Emacs org-mode compatible table (also orgtbl-mode)
*/
func printOrgmodeData(data Tabdata) {
tableString := &strings.Builder{}
table := tablewriter.NewWriter(tableString)
table.SetHeader(data.headers)
for _, row := range data.entries {
table.Append(trimRow(row))
}
table.Render()
/* fix output for org-mode (orgtbl)
tableWriter output:
+------+------+
| cell | cell |
+------+------+
Needed for org-mode compatibility:
|------+------|
| cell | cell |
|------+------|
*/
leftR := regexp.MustCompile("(?m)^\\+")
rightR := regexp.MustCompile("\\+(?m)$")
fmt.Print(rightR.ReplaceAllString(leftR.ReplaceAllString(tableString.String(), "|"), "|"))
}
/*
Markdown table
*/
func printMarkdownData(data Tabdata) {
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader(data.headers)
for _, row := range data.entries {
table.Append(trimRow(row))
}
table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
table.SetCenterSeparator("|")
table.Render()
}
/*
Simple ASCII table without any borders etc, just like the input we expect
*/
func printAsciiData(data Tabdata) {
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader(data.headers)
table.AppendBulk(data.entries)
// for _, row := range data.entries {
// table.Append(trimRow(row))
// }
table.SetAutoWrapText(false)
table.SetAutoFormatHeaders(true)
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.SetAlignment(tablewriter.ALIGN_LEFT)
table.SetCenterSeparator("")
table.SetColumnSeparator("")
table.SetRowSeparator("")
table.SetHeaderLine(false)
table.SetBorder(false)
table.SetTablePadding("\t") // pad with tabs
table.SetNoWhiteSpace(true)
table.Render()
}
/*
We simulate the \x command of psql (the PostgreSQL client)
*/

View File

@@ -10,9 +10,12 @@ tablizer - Manipulate tabular output of other programs
Flags:
-c, --columns string Only show the speficied columns (separated by ,)
-d, --debug Enable debugging
-x, --extended Enable extended output
-X, --extended Enable extended output
-h, --help help for tablizer
-M, --markdown Enable markdown table output
-n, --no-numbering Disable header numbering
-O, --orgtbl Enable org-mode table output
-o, --output string Output mode - one of: orgtbl, markdown, extended, ascii(default)
-s, --separator string Custom field separator
-v, --version Print program version
@@ -70,12 +73,12 @@ The numbering can be suppressed by using the B<-n> option.
There might be cases when the tabular output of a program is way too
large for your current terminal but you still need to see every
column. In such cases the B<-x> option can be usefull which enables
I<extended mode>. In this mode, each row will be printed vertically,
header left, value right, aligned by the field widths. Here's an
example:
column. In such cases the B<-X> option (or B<-o extended> can be
usefull which enables I<extended mode>. In this mode, each row will be
printed vertically, header left, value right, aligned by the field
widths. Here's an example:
kubectl get pods | ./tablizer -x
kubectl get pods | ./tablizer -X
NAME: repldepl-7bcd8d5b64-7zq4l
READY: 1/1
STATUS: Running
@@ -85,6 +88,10 @@ example:
You can of course still use a regex to reduce the number of rows
displayed.
Beside normal ascii mode (the default) and extended mode there more
output modes available: B<orgtbl> which prints an Emacs org-mode table
and B<markdown> which prints a Markdown table.
Finally the B<-d> option enables debugging output which is mostly
usefull for the developer.