diff --git a/cli/command/container/diff.go b/cli/command/container/diff.go index 95926f5867..816a0a56a3 100644 --- a/cli/command/container/diff.go +++ b/cli/command/container/diff.go @@ -1,11 +1,9 @@ package container import ( - "fmt" - "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" - "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/cli/command/formatter" "github.com/pkg/errors" "github.com/spf13/cobra" "golang.org/x/net/context" @@ -40,19 +38,9 @@ func runDiff(dockerCli *command.DockerCli, opts *diffOptions) error { if err != nil { return err } - - for _, change := range changes { - var kind string - switch change.Kind { - case archive.ChangeModify: - kind = "C" - case archive.ChangeAdd: - kind = "A" - case archive.ChangeDelete: - kind = "D" - } - fmt.Fprintln(dockerCli.Out(), kind, change.Path) + diffCtx := formatter.Context{ + Output: dockerCli.Out(), + Format: formatter.NewDiffFormat("{{.Type}} {{.Path}}"), } - - return nil + return formatter.DiffWrite(diffCtx, changes) } diff --git a/cli/command/formatter/diff.go b/cli/command/formatter/diff.go new file mode 100644 index 0000000000..9b4681934c --- /dev/null +++ b/cli/command/formatter/diff.go @@ -0,0 +1,72 @@ +package formatter + +import ( + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/pkg/archive" +) + +const ( + defaultDiffTableFormat = "table {{.Type}}\t{{.Path}}" + + changeTypeHeader = "CHANGE TYPE" + pathHeader = "PATH" +) + +// NewDiffFormat returns a format for use with a diff Context +func NewDiffFormat(source string) Format { + switch source { + case TableFormatKey: + return defaultDiffTableFormat + } + return Format(source) +} + +// DiffWrite writes formatted diff using the Context +func DiffWrite(ctx Context, changes []container.ContainerChangeResponseItem) error { + + render := func(format func(subContext subContext) error) error { + for _, change := range changes { + if err := format(&diffContext{c: change}); err != nil { + return err + } + } + return nil + } + return ctx.Write(newDiffContext(), render) +} + +type diffContext struct { + HeaderContext + c container.ContainerChangeResponseItem +} + +func newDiffContext() *diffContext { + diffCtx := diffContext{} + diffCtx.header = map[string]string{ + "Type": changeTypeHeader, + "Path": pathHeader, + } + return &diffCtx +} + +func (d *diffContext) MarshalJSON() ([]byte, error) { + return marshalJSON(d) +} + +func (d *diffContext) Type() string { + var kind string + switch d.c.Kind { + case archive.ChangeModify: + kind = "C" + case archive.ChangeAdd: + kind = "A" + case archive.ChangeDelete: + kind = "D" + } + return kind + +} + +func (d *diffContext) Path() string { + return d.c.Path +} diff --git a/cli/command/formatter/diff_test.go b/cli/command/formatter/diff_test.go new file mode 100644 index 0000000000..52080354f5 --- /dev/null +++ b/cli/command/formatter/diff_test.go @@ -0,0 +1,59 @@ +package formatter + +import ( + "bytes" + "testing" + + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/pkg/testutil/assert" +) + +func TestDiffContextFormatWrite(t *testing.T) { + // Check default output format (verbose and non-verbose mode) for table headers + cases := []struct { + context Context + expected string + }{ + { + Context{Format: NewDiffFormat("table")}, + `CHANGE TYPE PATH +C /var/log/app.log +A /usr/app/app.js +D /usr/app/old_app.js +`, + }, + { + Context{Format: NewDiffFormat("table {{.Path}}")}, + `PATH +/var/log/app.log +/usr/app/app.js +/usr/app/old_app.js +`, + }, + { + Context{Format: NewDiffFormat("{{.Type}}: {{.Path}}")}, + `C: /var/log/app.log +A: /usr/app/app.js +D: /usr/app/old_app.js +`, + }, + } + + diffs := []container.ContainerChangeResponseItem{ + {archive.ChangeModify, "/var/log/app.log"}, + {archive.ChangeAdd, "/usr/app/app.js"}, + {archive.ChangeDelete, "/usr/app/old_app.js"}, + } + + for _, testcase := range cases { + out := bytes.NewBufferString("") + testcase.context.Output = out + err := DiffWrite(testcase.context, diffs) + if err != nil { + assert.Error(t, err, testcase.expected) + } else { + assert.Equal(t, out.String(), testcase.expected) + } + } +}