1
1
mirror of https://github.com/wader/fq.git synced 2024-11-30 09:58:13 +03:00
fq/pkg/interp/preview.go
2021-09-12 13:08:50 +02:00

180 lines
3.8 KiB
Go

package interp
import (
"encoding/hex"
"fmt"
"io"
"sort"
"strconv"
"strings"
"github.com/wader/fq/internal/num"
"github.com/wader/fq/pkg/bitio"
"github.com/wader/fq/pkg/decode"
)
type previewNode struct {
name string
pos int64
count int
previews map[string]struct{}
nodes map[string]*previewNode
}
func previewValue(v *decode.Value) string {
switch vv := v.V.(type) {
case decode.Array:
return "[]"
case decode.Struct:
return v.Description
case bool:
if vv {
return "true"
} else {
return "false"
}
case int64:
// TODO: DisplayFormat is weird
return num.PadFormatInt(vv, decode.DisplayFormatToBase(v.DisplayFormat), true, 0)
case uint64:
return num.PadFormatUint(vv, decode.DisplayFormatToBase(v.DisplayFormat), true, 0)
case float64:
// TODO: float32? better truncated to significant digits?
return strconv.FormatFloat(vv, 'g', -1, 64)
case string:
if len(vv) > 50 {
return fmt.Sprintf("%q", vv[0:50]) + "..."
} else {
return fmt.Sprintf("%q", vv)
}
case []byte:
if len(vv) > 16 {
return hex.EncodeToString(vv[0:16]) + "..."
} else {
return hex.EncodeToString(vv)
}
case *bitio.Buffer:
vvLen := vv.Len()
if vvLen > 16*8 {
bs, _ := vv.BytesRange(0, 16)
return hex.EncodeToString(bs) + "..."
} else {
bs, _ := vv.BytesRange(0, int(bitio.BitsByteCount(vvLen)))
return hex.EncodeToString(bs)
}
case nil:
return "none"
case []interface{}:
// TODO: remove?
return "json []"
case map[string]interface{}:
// TODO: remove?
return "json {}"
default:
panic("unreachable")
}
}
func preview(v *decode.Value, w io.Writer, _ Options) error {
switch v.V.(type) {
case decode.Struct:
sn := &previewNode{name: ".", count: -1, previews: map[string]struct{}{}, nodes: map[string]*previewNode{}}
err := previewEx(v, sn, 0)
if err != nil {
return err
}
var printFn func(s *previewNode, depth int)
printFn = func(s *previewNode, depth int) {
indent := strings.Repeat(" ", depth)
fmt.Fprint(w, indent)
fmt.Fprint(w, s.name)
if s.count != -1 {
fmt.Fprintf(w, "[%d]", s.count)
}
var sortedPreviews []string
for p := range s.previews {
sortedPreviews = append(sortedPreviews, p)
}
sort.Strings(sortedPreviews)
if len(sortedPreviews) > 10 {
sortedPreviews = sortedPreviews[0:10]
sortedPreviews = append(sortedPreviews, "...")
}
if len(sortedPreviews) > 0 {
fmt.Fprintf(w, " (%s)", strings.Join(sortedPreviews, ","))
}
fmt.Fprintln(w)
var sortedNodes []*previewNode
for _, n := range s.nodes {
sortedNodes = append(sortedNodes, n)
}
// sort.Slice(sorted, func(i, j int) bool {
// return sorted[i].name < sorted[j].name
// })
sort.Slice(sortedNodes, func(i, j int) bool {
return sortedNodes[i].pos < sortedNodes[j].pos
})
for _, n := range sortedNodes {
printFn(n, depth+1)
}
}
printFn(sn, 0)
default:
fmt.Fprintln(w, previewValue(v))
}
return nil
}
func previewEx(v *decode.Value, n *previewNode, depth int) error {
if _, ok := v.V.(decode.Array); !ok {
p := previewValue(v)
if p != "" {
if _, ok := n.previews[p]; !ok {
n.previews[p] = struct{}{}
}
}
}
switch vv := v.V.(type) {
case decode.Struct:
for _, sv := range vv {
sn, ok := n.nodes[sv.Name]
if !ok {
sn = &previewNode{
name: sv.Name,
pos: sv.Range.Start,
count: -1,
previews: map[string]struct{}{},
nodes: map[string]*previewNode{},
}
if a, ok := sv.V.(decode.Array); ok {
sn.count = len(a)
}
n.nodes[sv.Name] = sn
}
err := previewEx(sv, sn, depth+1)
if err != nil {
return err
}
}
case decode.Array:
for _, av := range vv {
err := previewEx(av, n, depth+1)
_ = n
if err != nil {
return err
}
}
}
return nil
}