212 lines
5.0 KiB
Go
212 lines
5.0 KiB
Go
package jsontable
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
|
|
"github.com/olekukonko/tablewriter"
|
|
)
|
|
|
|
// A quick-and-dirty proxy/emulator of tablewriter to enable more easy machine readable output
|
|
// - Does not strictly support types, just quoted or unquoted values
|
|
// - Does not support nested values.
|
|
// If a datalabel is set with SetDataLabel(true, "..."), that will be used as the key for teh data of the table,
|
|
// otherwise if the caption is set with SetCaption(true, "..."), the data label will be set to the default of
|
|
// "rows", otherwise the table will output as a JSON list.
|
|
//
|
|
// Proxys all actions through to the tablewriter except addrow and addbatch, which it does at render time
|
|
//
|
|
|
|
type JSONTable struct {
|
|
out io.Writer
|
|
colsize int
|
|
rows [][]string
|
|
keys []string
|
|
quoted []bool // hack to do output typing, quoted vs. unquoted
|
|
hasDataLabel bool
|
|
dataLabel string
|
|
hasCaption bool
|
|
caption string // the actual caption
|
|
hasCaptionLabel bool
|
|
captionLabel string // the key in the dictionary for the caption
|
|
tbl *tablewriter.Table
|
|
}
|
|
|
|
func writeChar(w io.Writer, c byte) {
|
|
w.Write([]byte{c})
|
|
|
|
}
|
|
|
|
func NewJSONTable(writer io.Writer) *JSONTable {
|
|
t := &JSONTable{
|
|
out: writer,
|
|
colsize: 0,
|
|
rows: [][]string{},
|
|
keys: []string{},
|
|
quoted: []bool{},
|
|
hasDataLabel: false,
|
|
dataLabel: "rows",
|
|
hasCaption: false,
|
|
caption: "",
|
|
hasCaptionLabel: false,
|
|
captionLabel: "caption",
|
|
tbl: tablewriter.NewWriter(writer),
|
|
}
|
|
return t
|
|
}
|
|
|
|
func (t *JSONTable) NumLines() int {
|
|
// JSON only but reflects a shared state.
|
|
return len(t.rows)
|
|
}
|
|
|
|
func (t *JSONTable) SetHeader(keys []string) {
|
|
// Set the keys value which will assign each column to the keys.
|
|
// Note that we'll ignore values that are beyond the length of the keys list
|
|
t.colsize = len(keys)
|
|
t.keys = []string{}
|
|
for _, k := range keys {
|
|
t.keys = append(t.keys, k)
|
|
t.quoted = append(t.quoted, true)
|
|
}
|
|
t.tbl.SetHeader(keys)
|
|
}
|
|
|
|
func (t *JSONTable) SetColumnQuoting(quoting []bool) {
|
|
// Specify which columns are quoted or unquoted in output
|
|
// JSON only
|
|
for i := 0; i < t.colsize; i++ {
|
|
t.quoted[i] = quoting[i]
|
|
}
|
|
}
|
|
|
|
func (t *JSONTable) Append(row []string) {
|
|
// We'll just append whatever to the rows list. If they fix the keys after appending rows, it'll work as
|
|
// expected.
|
|
// We should detect if the row is narrower than the key list tho.
|
|
// JSON only (but we use the rows later when rendering a regular table)
|
|
t.rows = append(t.rows, row)
|
|
}
|
|
|
|
func (t *JSONTable) Render() {
|
|
// Load the table with rows and render.
|
|
// Proxy only
|
|
for _, row := range t.rows {
|
|
t.tbl.Append(row)
|
|
}
|
|
|
|
t.tbl.Render()
|
|
}
|
|
|
|
func (t *JSONTable) _JSONRenderInner() {
|
|
// JSON only
|
|
// Render the list of dictionaries to the writer.
|
|
//// inner render loop
|
|
writeChar(t.out, '[')
|
|
for rowidx, row := range t.rows {
|
|
if rowidx != 0 {
|
|
writeChar(t.out, ',')
|
|
}
|
|
writeChar(t.out, '{')
|
|
for keyidx, key := range t.keys {
|
|
key := strings.ToLower(key)
|
|
key = strings.ReplaceAll(key, " ", "-")
|
|
|
|
value := "nil"
|
|
if keyidx < len(row) {
|
|
value = row[keyidx]
|
|
}
|
|
if keyidx != 0 {
|
|
writeChar(t.out, ',')
|
|
}
|
|
if t.quoted[keyidx] {
|
|
fmt.Fprintf(t.out, "\"%s\":\"%s\"", key, value)
|
|
} else {
|
|
fmt.Fprintf(t.out, "\"%s\":%s", key, value)
|
|
}
|
|
}
|
|
writeChar(t.out, '}')
|
|
}
|
|
writeChar(t.out, ']')
|
|
|
|
}
|
|
|
|
func (t *JSONTable) JSONRender() {
|
|
// write JSON table to output
|
|
// JSON only
|
|
|
|
if t.hasDataLabel || t.hasCaption {
|
|
// dict mode
|
|
writeChar(t.out, '{')
|
|
|
|
if t.hasCaption {
|
|
fmt.Fprintf(t.out, "\"%s\":\"%s\",", t.captionLabel, t.caption)
|
|
}
|
|
fmt.Fprintf(t.out, "\"%s\":", t.dataLabel)
|
|
}
|
|
|
|
// write list
|
|
t._JSONRenderInner()
|
|
|
|
if t.hasDataLabel || t.hasCaption {
|
|
// dict mode
|
|
writeChar(t.out, '}')
|
|
}
|
|
|
|
}
|
|
|
|
func (t *JSONTable) SetCaption(caption bool, captionText ...string) {
|
|
t.hasCaption = caption
|
|
if len(captionText) == 1 {
|
|
t.caption = captionText[0]
|
|
}
|
|
t.tbl.SetCaption(caption, captionText...)
|
|
}
|
|
|
|
func (t *JSONTable) SetCaptionLabel(captionLabel bool, captionLabelText ...string) {
|
|
// JSON only
|
|
t.hasCaptionLabel = captionLabel
|
|
if len(captionLabelText) == 1 {
|
|
t.captionLabel = captionLabelText[0]
|
|
}
|
|
}
|
|
|
|
func (t *JSONTable) SetDataLabel(dataLabel bool, dataLabelText ...string) {
|
|
// JSON only
|
|
t.hasDataLabel = dataLabel
|
|
if len(dataLabelText) == 1 {
|
|
t.dataLabel = dataLabelText[0]
|
|
}
|
|
}
|
|
|
|
func (t *JSONTable) AppendBulk(rows [][]string) {
|
|
// JSON only but reflects shared state
|
|
for _, row := range rows {
|
|
t.Append(row)
|
|
}
|
|
}
|
|
|
|
// Stuff we should implement but we just proxy for now.
|
|
func (t *JSONTable) SetAutoMergeCellsByColumnIndex(cols []int) {
|
|
// FIXME
|
|
t.tbl.SetAutoMergeCellsByColumnIndex(cols)
|
|
}
|
|
|
|
// Stuff we should implement but we just proxy for now.
|
|
func (t *JSONTable) SetAlignment(align int) {
|
|
// FIXME
|
|
t.tbl.SetAlignment(align)
|
|
}
|
|
|
|
func (t *JSONTable) SetAutoMergeCells(auto bool) {
|
|
// FIXME
|
|
t.tbl.SetAutoMergeCells(auto)
|
|
}
|
|
|
|
// Stub functions
|
|
func (t *JSONTable) SetAutoWrapText(auto bool) {
|
|
t.tbl.SetAutoWrapText(auto)
|
|
return
|
|
}
|