Skip to content

Commit

Permalink
implement treewriter
Browse files Browse the repository at this point in the history
Nothing mergeable here, but this was me playing around; initially to see
if we could print the format such as suggested in docker#5560 (comment)

That output is shown in `TestTree`:

    IMAGE/TAGS ID                                    DISK USAGE          CONTENT SIZE        USED
    alpine:latest                                    beefdbd8a1da        13.6MB              4.09MB
    ├─ linux/amd64                                   33735bd63cf8        0B                  0B
    ├─ linux/arm/v6                                  50f635c8b04d        0B                  0B
    ├─ linux/arm/v7                                  f2f82d424957        0B                  0B
    ├─ linux/arm64/v8                                9cee2b382fe2        13.6MB              4.09MB
    ├─ linux/386                                     b3e87f642f5c        0B                  0B
    ├─ linux/ppc64le                                 c7a6800e3dc5        0B                  0B
    ├─ linux/riscv64                                 80cde017a105        0B                  0B
    └─ linux/s390x                                   2b5b26e09ca2        0B                  0B

    namespace/image                                  beefdbd8a1da        13.6MB              4.09MB
    ├─ namespace/image:1                             beefdbd8a1da        -                   -
    ├─ namespace/image:1.0                           beefdbd8a1da        -                   -
    ├─ namespace/image:1.0.0                         beefdbd8a1da        -                   -
    └─ namespace/image:latest                        beefdbd8a1da        -                   -
       ├─ linux/amd64                                33735bd63cf8        0B                  0B
       ├─ linux/arm/v6                               50f635c8b04d        0B                  0B
       ├─ linux/arm/v7                               f2f82d424957        0B                  0B
       ├─ linux/arm64/v8                             9cee2b382fe2        13.6MB              4.09MB
       ├─ linux/386                                  b3e87f642f5c        0B                  0B
       ├─ linux/ppc64le                              c7a6800e3dc5        0B                  0B
       ├─ linux/riscv64                              80cde017a105        0B                  0B
       └─ linux/s390x                                2b5b26e09ca2        0B                  0B

    internal.example.com/namespace/image             beefdbd8a1da        13.6MB              4.09MB
    ├─ internal.example.com/namespace/image:1        beefdbd8a1da        -                   -
    ├─ internal.example.com/namespace/image:1.0      beefdbd8a1da        -                   -
    ├─ internal.example.com/namespace/image:1.0.0    beefdbd8a1da        -                   -
    └─ internal.example.com/namespace/image:latest   beefdbd8a1da        -                   -
       ├─ linux/amd64                                33735bd63cf8        0B                  0B
       ├─ linux/arm/v6                               50f635c8b04d        0B                  0B
       ├─ linux/arm/v7                               f2f82d424957        0B                  0B
       ├─ linux/arm64/v8                             9cee2b382fe2        13.6MB              4.09MB
       ├─ linux/386                                  b3e87f642f5c        0B                  0B
       ├─ linux/ppc64le                              c7a6800e3dc5        0B                  0B
       ├─ linux/riscv64                              80cde017a105        0B                  0B
       └─ linux/s390x                                2b5b26e09ca2        0B                  0B

The second bit was to see if we could make the tree output more align
with other output formats;

- Most of our commands allow passing a `--format`, including for (e.g.) `table`
- We want the tree view to also support, e.g. `--no-trunc`, which means that
  some columns will be wider.
- If we use a tabwriter for printing, we can have it handle the column-sizing
  for us.
- And if we do, we could let the user pass a custom format, and still print
  it as a tree.

e.g., a format could be;

    --format 'tree {.Image}}\t{{.Digest}}\t{{.InUse}}'

Which would output something like

    IMAGE                   ID             USED
    alpine:latest           beefdbd8a1da    ✔
    ├─ linux/amd64          33735bd63cf8
    ├─ linux/arm/v6         50f635c8b04d
    ├─ linux/arm/v7         f2f82d424957
    ├─ linux/arm64/v8       9cee2b382fe2    ✔
    ├─ linux/386            b3e87f642f5c
    ├─ linux/ppc64le        c7a6800e3dc5
    ├─ linux/riscv64        80cde017a105
    └─ linux/s390x          2b5b26e09ca2

The `TestTree` implementation is really quirky though, as it uses a `[][]string`,
which won't work well if we want to make it more generic (with an "unknown"
depth); probably needs some type defined that has an optional slice for child
rows.

Signed-off-by: Sebastiaan van Stijn <[email protected]>
  • Loading branch information
thaJeztah committed Oct 28, 2024
1 parent 32ff200 commit cd7a26e
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 0 deletions.
1 change: 1 addition & 0 deletions cli/command/formatter/treewriter/treewriter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package treewriter
88 changes: 88 additions & 0 deletions cli/command/formatter/treewriter/treewriter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package treewriter

import (
"bytes"
"fmt"
"strings"
"testing"

"github.com/docker/cli/cli/command/formatter/tabwriter"
)

func TestTree(t *testing.T) {
var header []string
var rows [][]string

header = []string{"IMAGE/TAGS", "ID", "DISK USAGE", "CONTENT SIZE", "USED"}

// TODO(thaJeztah): using [][]string doesn't work well for this; we probably
// need to create a type for this that has "optional" child records that
// we can recurse over to build the tree.
rows = append(rows, header)
rows = append(rows, [][]string{
{"", "alpine:latest", "beefdbd8a1da", "13.6MB", "4.09MB"},
{"├─", "linux/amd64", "33735bd63cf8", "0B", "0B"},
{"├─", "linux/arm/v6", "50f635c8b04d", "0B", "0B"},
{"├─", "linux/arm/v7", "f2f82d424957", "0B", "0B"},
{"├─", "linux/arm64/v8", "9cee2b382fe2", "13.6MB", "4.09MB"},
{"├─", "linux/386", "b3e87f642f5c", "0B", "0B"},
{"├─", "linux/ppc64le", "c7a6800e3dc5", "0B", "0B"},
{"├─", "linux/riscv64", "80cde017a105", "0B", "0B"},
{"└─", "linux/s390x", "2b5b26e09ca2", "0B", "0B"},

{"", "namespace/image", "beefdbd8a1da", "13.6MB", "4.09MB"},
{"├─", "namespace/image:1", "beefdbd8a1da", "-", "-"},
{"├─", "namespace/image:1.0", "beefdbd8a1da", "-", "-"},
{"├─", "namespace/image:1.0.0", "beefdbd8a1da", "-", "-"},
{"└─", "namespace/image:latest", "beefdbd8a1da", "-", "-"},
{" ├─", "linux/amd64", "33735bd63cf8", "0B", "0B"},
{" ├─", "linux/arm/v6", "50f635c8b04d", "0B", "0B"},
{" ├─", "linux/arm/v7", "f2f82d424957", "0B", "0B"},
{" ├─", "linux/arm64/v8", "9cee2b382fe2", "13.6MB", "4.09MB"},
{" ├─", "linux/386", "b3e87f642f5c", "0B", "0B"},
{" ├─", "linux/ppc64le", "c7a6800e3dc5", "0B", "0B"},
{" ├─", "linux/riscv64", "80cde017a105", "0B", "0B"},
{" └─", "linux/s390x", "2b5b26e09ca2", "0B", "0B"},

{"", "internal.example.com/namespace/image", "beefdbd8a1da", "13.6MB", "4.09MB"},
{"├─", "internal.example.com/namespace/image:1", "beefdbd8a1da", "-", "-"},
{"├─", "internal.example.com/namespace/image:1.0", "beefdbd8a1da", "-", "-"},
{"├─", "internal.example.com/namespace/image:1.0.0", "beefdbd8a1da", "-", "-"},
{"└─", "internal.example.com/namespace/image:latest", "beefdbd8a1da", "-", "-"},
{" ├─", "linux/amd64", "33735bd63cf8", "0B", "0B"},
{" ├─", "linux/arm/v6", "50f635c8b04d", "0B", "0B"},
{" ├─", "linux/arm/v7", "f2f82d424957", "0B", "0B"},
{" ├─", "linux/arm64/v8", "9cee2b382fe2", "13.6MB", "4.09MB"},
{" ├─", "linux/386", "b3e87f642f5c", "0B", "0B"},
{" ├─", "linux/ppc64le", "c7a6800e3dc5", "0B", "0B"},
{" ├─", "linux/riscv64", "80cde017a105", "0B", "0B"},
{" └─", "linux/s390x", "2b5b26e09ca2", "0B", "0B"},
}...)

buf := bytes.NewBuffer(nil)
// buf.WriteString("Create a context\n\nDocker endpoint config:\n\n")
tw := tabwriter.NewWriter(buf, 20, 1, 3, ' ', 0)
for rowNum, cols := range rows {
if len(cols) == 0 {
continue
}

var treeFix string
treeFix, cols = cols[0], cols[1:]
if treeFix == "" {
// Start of new group
if rowNum > 1 {
// Print an empty line between groups. We need to write a tab
// for each column for the tab-writer to give all groups equal
// widths.
_, _ = fmt.Fprintln(tw, strings.Repeat("\t", len(cols)))
}
_, _ = fmt.Fprintln(tw, strings.Join(cols, "\t"))
} else {
_, _ = fmt.Fprintln(tw, treeFix, strings.Join(cols, "\t"))
}

}
_ = tw.Flush()
fmt.Println(buf.String())
}

0 comments on commit cd7a26e

Please sign in to comment.