diff --git a/cli/command/formatter/treewriter/treewriter.go b/cli/command/formatter/treewriter/treewriter.go new file mode 100644 index 000000000000..b7349e483192 --- /dev/null +++ b/cli/command/formatter/treewriter/treewriter.go @@ -0,0 +1 @@ +package treewriter diff --git a/cli/command/formatter/treewriter/treewriter_test.go b/cli/command/formatter/treewriter/treewriter_test.go new file mode 100644 index 000000000000..1e5462ead8f6 --- /dev/null +++ b/cli/command/formatter/treewriter/treewriter_test.go @@ -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()) +}