mirror of
https://codeberg.org/scip/tablizer.git
synced 2025-12-17 04:30:56 +01:00
refactored output printer, now using tablewriter
This commit is contained in:
17
cmd/root.go
17
cmd/root.go
@@ -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
2
go.mod
@@ -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
4
go.sum
@@ -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=
|
||||
|
||||
@@ -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)"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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++
|
||||
}
|
||||
|
||||
188
lib/printer.go
188
lib/printer.go
@@ -19,78 +19,146 @@ 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]
|
||||
}
|
||||
|
||||
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 {
|
||||
if len(Columns) > 0 {
|
||||
if !contains(UseColumns, i+1) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
fmt.Printf(formats[idx], strings.TrimSpace(value))
|
||||
idx++
|
||||
}
|
||||
fmt.Println()
|
||||
numberedHeaders = append(numberedHeaders, fmt.Sprintf("%s(%d)", head, i+1))
|
||||
}
|
||||
data.headers = numberedHeaders
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
reducedEntries = append(reducedEntries, reducedEntry)
|
||||
}
|
||||
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()
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
19
tablizer.pod
19
tablizer.pod
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user