Skip to content
This repository has been archived by the owner on Feb 17, 2024. It is now read-only.

order table output of get command #31

Merged
merged 1 commit into from
May 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 71 additions & 56 deletions internal/cmd/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ Examples:
"digest": "sha256:9d7b7563baf3702cf24ed3688dc9a58faef2d0ac586041cb2dc95df919f5e5f2",
"size": 7150231,
"annotations": {
"timecraft.module.name": "app.wasm",
"timecraft.object.created-at": "2023-05-28T21:52:26Z"
"timecraft.module.name": "app.wasm"
}
}
Expand Down Expand Up @@ -146,88 +145,104 @@ Did you mean?%s`, resourceTypeLookup, joinResourceTypes(matchingResources, "\n
return err
}

func getConfigs(ctx context.Context, w io.Writer, r *timemachine.Registry) stream.WriteCloser[*format.Descriptor] {
func getConfigs(ctx context.Context, w io.Writer, reg *timemachine.Registry) stream.WriteCloser[*format.Descriptor] {
type config struct {
ID string `text:"CONFIG ID"`
Runtime string `text:"RUNTIME"`
Modules int `text:"MODULES"`
Size human.Bytes `text:"SIZE"`
}
return newDescTableWriter(w, func(desc *format.Descriptor) (config, error) {
c, err := r.LookupConfig(ctx, desc.Digest)
if err != nil {
return config{}, err
}
r, err := r.LookupRuntime(ctx, c.Runtime.Digest)
if err != nil {
return config{}, err
}
return config{
ID: desc.Digest.Short(),
Runtime: r.Runtime + " (" + r.Version + ")",
Modules: len(c.Modules),
Size: human.Bytes(desc.Size),
}, nil
})
return newDescTableWriter(w,
func(c1, c2 config) bool {
return c1.ID < c2.ID
},
func(desc *format.Descriptor) (config, error) {
c, err := reg.LookupConfig(ctx, desc.Digest)
if err != nil {
return config{}, err
}
r, err := reg.LookupRuntime(ctx, c.Runtime.Digest)
if err != nil {
return config{}, err
}
return config{
ID: desc.Digest.Short(),
Runtime: r.Runtime + " (" + r.Version + ")",
Modules: len(c.Modules),
Size: human.Bytes(desc.Size),
}, nil
})
}

func getModules(ctx context.Context, w io.Writer, r *timemachine.Registry) stream.WriteCloser[*format.Descriptor] {
func getModules(ctx context.Context, w io.Writer, reg *timemachine.Registry) stream.WriteCloser[*format.Descriptor] {
type module struct {
ID string `text:"MODULE ID"`
Name string `text:"MODULE NAME"`
Size human.Bytes `text:"SIZE"`
}
return newDescTableWriter(w, func(desc *format.Descriptor) (module, error) {
name := desc.Annotations["timecraft.module.name"]
if name == "" {
name = "(none)"
}
return module{
ID: desc.Digest.Short(),
Name: name,
Size: human.Bytes(desc.Size),
}, nil
})
return newDescTableWriter(w,
func(m1, m2 module) bool {
return m1.ID < m2.ID
},
func(desc *format.Descriptor) (module, error) {
name := desc.Annotations["timecraft.module.name"]
if name == "" {
name = "(none)"
}
return module{
ID: desc.Digest.Short(),
Name: name,
Size: human.Bytes(desc.Size),
}, nil
})
}

func getProcesses(ctx context.Context, w io.Writer, r *timemachine.Registry) stream.WriteCloser[*format.Descriptor] {
func getProcesses(ctx context.Context, w io.Writer, reg *timemachine.Registry) stream.WriteCloser[*format.Descriptor] {
type process struct {
ID format.UUID `text:"PROCESS ID"`
StartTime human.Time `text:"STARTED"`
}
return newDescTableWriter(w, func(desc *format.Descriptor) (process, error) {
p, err := r.LookupProcess(ctx, desc.Digest)
if err != nil {
return process{}, err
}
return process{
ID: p.ID,
StartTime: human.Time(p.StartTime),
}, nil
})
return newDescTableWriter(w,
func(p1, p2 process) bool {
return time.Time(p1.StartTime).Before(time.Time(p2.StartTime))
},
func(desc *format.Descriptor) (process, error) {
p, err := reg.LookupProcess(ctx, desc.Digest)
if err != nil {
return process{}, err
}
return process{
ID: p.ID,
StartTime: human.Time(p.StartTime),
}, nil
})
}

func getRuntimes(ctx context.Context, w io.Writer, r *timemachine.Registry) stream.WriteCloser[*format.Descriptor] {
func getRuntimes(ctx context.Context, w io.Writer, reg *timemachine.Registry) stream.WriteCloser[*format.Descriptor] {
type runtime struct {
ID string `text:"RUNTIME ID"`
Runtime string `text:"RUNTIME NAME"`
Version string `text:"VERSION"`
}
return newDescTableWriter(w, func(desc *format.Descriptor) (runtime, error) {
r, err := r.LookupRuntime(ctx, desc.Digest)
if err != nil {
return runtime{}, err
}
return runtime{
ID: desc.Digest.Short(),
Runtime: r.Runtime,
Version: r.Version,
}, nil
})
return newDescTableWriter(w,
func(r1, r2 runtime) bool {
return r1.ID < r2.ID
},
func(desc *format.Descriptor) (runtime, error) {
r, err := reg.LookupRuntime(ctx, desc.Digest)
if err != nil {
return runtime{}, err
}
return runtime{
ID: desc.Digest.Short(),
Runtime: r.Runtime,
Version: r.Version,
}, nil
})
}

func newDescTableWriter[T any](w io.Writer, conv func(*format.Descriptor) (T, error)) stream.WriteCloser[*format.Descriptor] {
tw := textprint.NewTableWriter[T](w)
func newDescTableWriter[T any](w io.Writer, orderBy func(T, T) bool, conv func(*format.Descriptor) (T, error)) stream.WriteCloser[*format.Descriptor] {
tw := textprint.NewTableWriter[T](w, textprint.OrderBy(orderBy))
cw := stream.ConvertWriter[T](tw, conv)
return stream.NewWriteCloser(cw, tw)
}
Expand Down
91 changes: 56 additions & 35 deletions internal/print/textprint/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,64 @@ import (
"text/tabwriter"

"github.com/stealthrocket/timecraft/internal/stream"
"golang.org/x/exp/slices"
)

func NewTableWriter[T any](w io.Writer) stream.WriteCloser[T] {
type TableOption[T any] func(*tableWriter[T])

func OrderBy[T any](f func(T, T) bool) TableOption[T] {
return func(t *tableWriter[T]) {
t.orderBy = f
}
}

func NewTableWriter[T any](w io.Writer, opts ...TableOption[T]) stream.WriteCloser[T] {
t := &tableWriter[T]{
writer: tabwriter.NewWriter(w, 0, 4, 2, ' ', 0),
valueOf: func(values []T, index int) reflect.Value {
return reflect.ValueOf(&values[index]).Elem()
},
output: w,
}
for _, opt := range opts {
opt(t)
}
return t
}

writeString := func(w io.Writer, s string) {
_, err := io.WriteString(w, s)
if err != nil {
panic(err)
}
type tableWriter[T any] struct {
output io.Writer
values []T
orderBy func(T, T) bool
}

func (t *tableWriter[T]) Write(values []T) (int, error) {
t.values = append(t.values, values...)
return len(values), nil
}

func (t *tableWriter[T]) Close() error {
tw := tabwriter.NewWriter(t.output, 0, 4, 2, ' ', 0)

if t.orderBy != nil {
slices.SortFunc(t.values, t.orderBy)
}

valueOf := func(values []T, index int) reflect.Value {
return reflect.ValueOf(&values[index]).Elem()
}

var v T
valueType := reflect.TypeOf(v)
if valueType.Kind() == reflect.Pointer {
valueType = valueType.Elem()
t.valueOf = func(values []T, index int) reflect.Value {
valueOf = func(values []T, index int) reflect.Value {
return reflect.ValueOf(values[index]).Elem()
}
}

var encoders []encodeFunc
for i, f := range reflect.VisibleFields(valueType) {
if i != 0 {
writeString(t.writer, "\t")
if _, err := io.WriteString(tw, "\t"); err != nil {
return err
}
}

name := f.Name
Expand All @@ -53,44 +82,36 @@ func NewTableWriter[T any](w io.Writer) stream.WriteCloser[T] {
continue
}

writeString(t.writer, name)
t.encoders = append(t.encoders, encodeFuncOfStructField(f.Type, f.Index))
if _, err := io.WriteString(tw, name); err != nil {
return err
}
encoders = append(encoders, encodeFuncOfStructField(f.Type, f.Index))
}

writeString(t.writer, "\n")
return t
}

type tableWriter[T any] struct {
writer *tabwriter.Writer
encoders []encodeFunc
valueOf func([]T, int) reflect.Value
}
if _, err := io.WriteString(tw, "\n"); err != nil {
return err
}

func (t *tableWriter[T]) Write(values []T) (int, error) {
for n := range values {
v := t.valueOf(values, n)
w := io.Writer(t.writer)
for n := range t.values {
v := valueOf(t.values, n)
w := io.Writer(tw)

for i, enc := range t.encoders {
for i, enc := range encoders {
if i != 0 {
_, err := io.WriteString(w, "\t")
if err != nil {
return n, err
return err
}
}
if err := enc(w, v); err != nil {
return n, err
return err
}
}

if _, err := io.WriteString(w, "\n"); err != nil {
return n, err
return err
}
}
return len(values), nil
}

func (t *tableWriter[T]) Close() error {
return t.writer.Flush()
return tw.Flush()
}
Loading