2020-08-06 20:58:47 +03:00
|
|
|
package tablew
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2023-04-16 01:28:51 +03:00
|
|
|
"strconv"
|
2020-08-06 20:58:47 +03:00
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/neilotoole/sq/cli/output"
|
|
|
|
"github.com/neilotoole/sq/libsq/source"
|
|
|
|
)
|
|
|
|
|
2023-04-16 01:28:51 +03:00
|
|
|
var _ output.SourceWriter = (*sourceWriter)(nil)
|
|
|
|
|
2020-08-06 20:58:47 +03:00
|
|
|
type sourceWriter struct {
|
2023-04-16 01:28:51 +03:00
|
|
|
tbl *table
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewSourceWriter returns a source writer that outputs source
|
|
|
|
// details in text table format.
|
2023-04-22 06:36:32 +03:00
|
|
|
func NewSourceWriter(out io.Writer, pr *output.Printing) output.SourceWriter {
|
|
|
|
tbl := &table{out: out, pr: pr, header: pr.ShowHeader}
|
2023-04-16 01:28:51 +03:00
|
|
|
w := &sourceWriter{tbl: tbl}
|
2020-08-06 20:58:47 +03:00
|
|
|
w.tbl.reset()
|
|
|
|
return w
|
|
|
|
}
|
|
|
|
|
2023-04-19 08:28:09 +03:00
|
|
|
// Collection implements output.SourceWriter.
|
|
|
|
func (w *sourceWriter) Collection(coll *source.Collection) error {
|
2023-04-22 06:36:32 +03:00
|
|
|
pr := w.tbl.pr
|
2023-04-19 08:28:09 +03:00
|
|
|
group := coll.ActiveGroup()
|
|
|
|
items, err := coll.SourcesInGroup(group)
|
2023-04-16 01:28:51 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
if !pr.Verbose {
|
2020-08-06 20:58:47 +03:00
|
|
|
// Print the short version
|
|
|
|
var rows [][]string
|
|
|
|
|
2023-04-16 01:28:51 +03:00
|
|
|
for _, src := range items {
|
2020-08-06 20:58:47 +03:00
|
|
|
row := []string{
|
|
|
|
src.Handle,
|
|
|
|
string(src.Type),
|
|
|
|
source.ShortLocation(src.Location),
|
|
|
|
}
|
|
|
|
|
2023-04-19 08:28:09 +03:00
|
|
|
if coll.Active() != nil && coll.Active().Handle == src.Handle {
|
2023-04-22 06:36:32 +03:00
|
|
|
row[0] = pr.Active.Sprintf(row[0])
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
rows = append(rows, row)
|
|
|
|
}
|
|
|
|
|
|
|
|
w.tbl.tblImpl.SetHeaderDisable(true)
|
2023-04-22 06:36:32 +03:00
|
|
|
w.tbl.tblImpl.SetColTrans(0, pr.Handle.SprintFunc())
|
|
|
|
w.tbl.tblImpl.SetColTrans(2, pr.Location.SprintFunc())
|
2020-08-06 20:58:47 +03:00
|
|
|
w.tbl.appendRowsAndRenderAll(rows)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Else print verbose
|
|
|
|
|
2023-04-16 01:28:51 +03:00
|
|
|
// "HANDLE", "ACTIVE", "DRIVER", "LOCATION", "OPTIONS"
|
2020-08-06 20:58:47 +03:00
|
|
|
var rows [][]string
|
2023-04-16 01:28:51 +03:00
|
|
|
for _, src := range items {
|
2020-08-06 20:58:47 +03:00
|
|
|
row := []string{
|
|
|
|
src.Handle,
|
2023-04-16 01:28:51 +03:00
|
|
|
"",
|
2020-08-06 20:58:47 +03:00
|
|
|
string(src.Type),
|
|
|
|
src.RedactedLocation(),
|
2022-12-18 11:35:59 +03:00
|
|
|
renderSrcOptions(src),
|
|
|
|
}
|
2020-08-06 20:58:47 +03:00
|
|
|
|
2023-04-19 08:28:09 +03:00
|
|
|
if coll.Active() != nil && coll.Active().Handle == src.Handle {
|
2023-04-22 06:36:32 +03:00
|
|
|
row[0] = pr.Active.Sprintf(row[0])
|
|
|
|
row[1] = pr.Bool.Sprintf("active")
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
rows = append(rows, row)
|
|
|
|
}
|
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
w.tbl.tblImpl.SetHeaderDisable(!pr.ShowHeader)
|
|
|
|
w.tbl.tblImpl.SetColTrans(0, pr.Handle.SprintFunc())
|
|
|
|
w.tbl.tblImpl.SetColTrans(3, pr.Location.SprintFunc())
|
2023-04-16 01:28:51 +03:00
|
|
|
w.tbl.tblImpl.SetHeader([]string{"HANDLE", "ACTIVE", "DRIVER", "LOCATION", "OPTIONS"})
|
2020-08-06 20:58:47 +03:00
|
|
|
w.tbl.appendRowsAndRenderAll(rows)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Source implements output.SourceWriter.
|
2023-04-19 08:28:09 +03:00
|
|
|
func (w *sourceWriter) Source(coll *source.Collection, src *source.Source) error {
|
2023-04-16 01:28:51 +03:00
|
|
|
if src == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var isActiveSrc bool
|
2023-04-19 08:28:09 +03:00
|
|
|
if coll != nil && coll.Active() == src {
|
2023-04-16 01:28:51 +03:00
|
|
|
isActiveSrc = true
|
|
|
|
}
|
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
if !w.tbl.pr.Verbose {
|
2020-08-06 20:58:47 +03:00
|
|
|
var rows [][]string
|
|
|
|
row := []string{
|
|
|
|
src.Handle,
|
|
|
|
string(src.Type),
|
|
|
|
source.ShortLocation(src.Location),
|
|
|
|
}
|
|
|
|
rows = append(rows, row)
|
2023-04-16 01:28:51 +03:00
|
|
|
|
|
|
|
if isActiveSrc {
|
2023-04-22 06:36:32 +03:00
|
|
|
w.tbl.tblImpl.SetColTrans(0, w.tbl.pr.Active.SprintFunc())
|
2023-04-16 01:28:51 +03:00
|
|
|
} else {
|
2023-04-22 06:36:32 +03:00
|
|
|
w.tbl.tblImpl.SetColTrans(0, w.tbl.pr.Handle.SprintFunc())
|
2023-04-16 01:28:51 +03:00
|
|
|
}
|
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
w.tbl.tblImpl.SetColTrans(2, w.tbl.pr.Location.SprintFunc())
|
2020-08-06 20:58:47 +03:00
|
|
|
w.tbl.tblImpl.SetHeaderDisable(true)
|
|
|
|
w.tbl.appendRowsAndRenderAll(rows)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var rows [][]string
|
|
|
|
row := []string{
|
|
|
|
src.Handle,
|
|
|
|
string(src.Type),
|
|
|
|
src.RedactedLocation(),
|
2022-12-18 11:35:59 +03:00
|
|
|
renderSrcOptions(src),
|
|
|
|
}
|
2020-08-06 20:58:47 +03:00
|
|
|
rows = append(rows, row)
|
|
|
|
|
2023-04-16 01:28:51 +03:00
|
|
|
if isActiveSrc {
|
2023-04-22 06:36:32 +03:00
|
|
|
w.tbl.tblImpl.SetColTrans(0, w.tbl.pr.Active.SprintFunc())
|
2023-04-16 01:28:51 +03:00
|
|
|
} else {
|
2023-04-22 06:36:32 +03:00
|
|
|
w.tbl.tblImpl.SetColTrans(0, w.tbl.pr.Handle.SprintFunc())
|
2023-04-16 01:28:51 +03:00
|
|
|
}
|
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
w.tbl.tblImpl.SetColTrans(2, w.tbl.pr.Location.SprintFunc())
|
2020-08-06 20:58:47 +03:00
|
|
|
w.tbl.tblImpl.SetHeaderDisable(true)
|
|
|
|
w.tbl.appendRowsAndRenderAll(rows)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-01-01 06:17:44 +03:00
|
|
|
// Removed implements output.SourceWriter.
|
|
|
|
func (w *sourceWriter) Removed(srcs ...*source.Source) error {
|
2023-04-22 06:36:32 +03:00
|
|
|
if !w.tbl.pr.Verbose || len(srcs) == 0 {
|
2023-01-01 06:17:44 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
w.tbl.pr.Faint.Fprint(w.tbl.out, "Removed ")
|
|
|
|
w.tbl.pr.Number.Fprint(w.tbl.out, len(srcs))
|
|
|
|
w.tbl.pr.Faint.Fprintln(w.tbl.out, " sources")
|
2023-01-01 06:17:44 +03:00
|
|
|
|
2023-04-16 01:28:51 +03:00
|
|
|
for _, src := range srcs {
|
2023-04-22 06:36:32 +03:00
|
|
|
w.tbl.pr.Handle.Fprintln(w.tbl.out, src.Handle)
|
2023-01-01 06:17:44 +03:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-08-06 20:58:47 +03:00
|
|
|
func renderSrcOptions(src *source.Source) string {
|
|
|
|
if src == nil || src.Options == nil || len(src.Options) == 0 {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
opts := make([]string, 0, len(src.Options))
|
|
|
|
|
2023-04-26 18:16:42 +03:00
|
|
|
for key, val := range src.Options {
|
2020-08-06 20:58:47 +03:00
|
|
|
if key == "" {
|
|
|
|
continue
|
|
|
|
}
|
2023-04-26 18:16:42 +03:00
|
|
|
v := val
|
2020-08-06 20:58:47 +03:00
|
|
|
// TODO: add color here to distinguish the keys/values
|
|
|
|
opts = append(opts, fmt.Sprintf("%s=%s", key, v))
|
|
|
|
}
|
|
|
|
return strings.Join(opts, " ")
|
|
|
|
}
|
2023-04-16 01:28:51 +03:00
|
|
|
|
|
|
|
// Group implements output.SourceWriter.
|
|
|
|
func (w *sourceWriter) Group(group *source.Group) error {
|
|
|
|
if group == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2023-04-22 06:36:32 +03:00
|
|
|
pr := w.tbl.pr
|
2023-04-16 01:28:51 +03:00
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
if !pr.Verbose {
|
2023-04-16 01:28:51 +03:00
|
|
|
if group.Active {
|
2023-04-22 06:36:32 +03:00
|
|
|
_, err := pr.Active.Fprintln(w.tbl.out, group)
|
2023-04-16 01:28:51 +03:00
|
|
|
return err
|
|
|
|
}
|
2023-04-22 06:36:32 +03:00
|
|
|
_, err := pr.Handle.Fprintln(w.tbl.out, group)
|
2023-04-16 01:28:51 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
// pr.Verbose is true
|
2023-04-16 01:28:51 +03:00
|
|
|
return w.renderGroups([]*source.Group{group})
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetActiveGroup implements output.SourceWriter.
|
|
|
|
func (w *sourceWriter) SetActiveGroup(group *source.Group) error {
|
2023-04-22 06:36:32 +03:00
|
|
|
if !w.tbl.pr.Verbose {
|
2023-04-16 01:28:51 +03:00
|
|
|
// Only print the group if --verbose
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
_, err := w.tbl.pr.Active.Fprintln(w.tbl.out, group)
|
2023-04-16 01:28:51 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *sourceWriter) renderGroups(groups []*source.Group) error {
|
2023-04-22 06:36:32 +03:00
|
|
|
pr := w.tbl.pr
|
2023-04-16 01:28:51 +03:00
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
if !pr.Verbose {
|
2023-04-16 01:28:51 +03:00
|
|
|
for _, group := range groups {
|
|
|
|
if group.Active {
|
2023-04-22 06:36:32 +03:00
|
|
|
pr.Active.Fprintln(w.tbl.out, group.Name)
|
2023-04-16 01:28:51 +03:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
pr.Handle.Fprintln(w.tbl.out, group.Name)
|
2023-04-16 01:28:51 +03:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verbose output
|
|
|
|
headers := []string{
|
|
|
|
"GROUP",
|
|
|
|
"SOURCES",
|
|
|
|
"TOTAL",
|
|
|
|
"SUBGROUPS",
|
|
|
|
"TOTAL",
|
|
|
|
"ACTIVE",
|
|
|
|
}
|
|
|
|
w.tbl.tblImpl.SetHeader(headers)
|
|
|
|
|
|
|
|
var rows [][]string
|
|
|
|
for _, g := range groups {
|
|
|
|
directSrcCount, totalSrcCount, directGroupCount, totalGroupCount := g.Counts()
|
|
|
|
row := []string{
|
|
|
|
g.Name,
|
|
|
|
strconv.Itoa(directSrcCount),
|
|
|
|
strconv.Itoa(totalSrcCount),
|
|
|
|
strconv.Itoa(directGroupCount),
|
|
|
|
strconv.Itoa(totalGroupCount),
|
|
|
|
strconv.FormatBool(g.Active),
|
|
|
|
}
|
|
|
|
|
|
|
|
if g.Active {
|
2023-04-22 06:36:32 +03:00
|
|
|
row[0] = pr.Active.Sprintf(row[0])
|
|
|
|
row[5] = pr.Bool.Sprintf("active")
|
2023-04-16 01:28:51 +03:00
|
|
|
} else {
|
|
|
|
// Don't render value for active==false. It's just noise.
|
|
|
|
row[5] = ""
|
|
|
|
}
|
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
rowEmptyZeroes(pr, row)
|
2023-04-16 01:28:51 +03:00
|
|
|
rows = append(rows, row)
|
|
|
|
}
|
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
w.tbl.tblImpl.SetColTrans(0, pr.Handle.SprintFunc())
|
|
|
|
w.tbl.tblImpl.SetColTrans(1, pr.Number.SprintFunc())
|
|
|
|
w.tbl.tblImpl.SetColTrans(2, pr.Number.SprintFunc())
|
|
|
|
w.tbl.tblImpl.SetColTrans(3, pr.Number.SprintFunc())
|
|
|
|
w.tbl.tblImpl.SetColTrans(4, pr.Number.SprintFunc())
|
|
|
|
w.tbl.tblImpl.SetColTrans(5, pr.Bool.SprintFunc())
|
2023-04-16 01:28:51 +03:00
|
|
|
|
|
|
|
w.tbl.appendRowsAndRenderAll(rows)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Groups implements output.SourceWriter.
|
|
|
|
func (w *sourceWriter) Groups(tree *source.Group) error {
|
|
|
|
groups := tree.AllGroups()
|
|
|
|
return w.renderGroups(groups)
|
|
|
|
}
|
|
|
|
|
|
|
|
// rowEmptyZeroes sets "0" to empty string. This seems to
|
|
|
|
// help with visual clutter.
|
2023-04-22 06:36:32 +03:00
|
|
|
func rowEmptyZeroes(_ *output.Printing, row []string) {
|
2023-04-16 01:28:51 +03:00
|
|
|
for i := range row {
|
|
|
|
if row[i] == "0" {
|
|
|
|
row[i] = ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
// rowEmptyZeroes prints "0" via pr.Faint. This seems to
|
2023-04-16 01:28:51 +03:00
|
|
|
// help with visual clutter.
|
2023-04-22 06:36:32 +03:00
|
|
|
func rowFaintZeroes(pr *output.Printing, row []string) { //nolint:unused
|
2023-04-16 01:28:51 +03:00
|
|
|
for i := range row {
|
|
|
|
if row[i] == "0" {
|
2023-04-22 06:36:32 +03:00
|
|
|
row[i] = pr.Faint.Sprintf(row[i])
|
2023-04-16 01:28:51 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|