From dbeb38d332f18b9f18278c9853270e50ed8a5170 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Wed, 27 Mar 2024 10:27:22 +0000 Subject: [PATCH] refactor: add stdout to metadata handlers Signed-off-by: Billy Zha --- cmd/oras/internal/display/handler.go | 16 ++-- .../internal/display/metadata/interface.go | 3 - .../internal/display/metadata/json/attach.go | 11 +-- .../internal/display/metadata/json/json.go | 27 +++++++ .../internal/display/metadata/json/push.go | 11 +-- .../display/metadata/template/attach.go | 11 +-- .../display/metadata/template/push.go | 11 +-- .../display/metadata/template/template.go | 31 +++++++ .../internal/display/metadata/text/attach.go | 12 +-- .../internal/display/metadata/text/push.go | 14 ++-- .../internal/display/metadata/view/printer.go | 80 ------------------- 11 files changed, 104 insertions(+), 123 deletions(-) create mode 100644 cmd/oras/internal/display/metadata/json/json.go create mode 100644 cmd/oras/internal/display/metadata/template/template.go delete mode 100644 cmd/oras/internal/display/metadata/view/printer.go diff --git a/cmd/oras/internal/display/handler.go b/cmd/oras/internal/display/handler.go index 78ef1bdf6..b1056012a 100644 --- a/cmd/oras/internal/display/handler.go +++ b/cmd/oras/internal/display/handler.go @@ -40,13 +40,13 @@ func NewPushHandler(format string, tty *os.File, out io.Writer, verbose bool) (s var metadataHandler metadata.PushHandler switch format { case "": - metadataHandler = text.NewPushHandler() + metadataHandler = text.NewPushHandler(out) case "json": - metadataHandler = json.NewPushHandler() + metadataHandler = json.NewPushHandler(out) default: - metadataHandler = template.NewPushHandler(format) + metadataHandler = template.NewPushHandler(format, out) } - metadataHandler.WithOutput(out) + return statusHandler, metadataHandler } @@ -64,12 +64,12 @@ func NewAttachHandler(format string, tty *os.File, out io.Writer, verbose bool) var metadataHandler metadata.AttachHandler switch format { case "": - metadataHandler = text.NewAttachHandler() + metadataHandler = text.NewAttachHandler(out) case "json": - metadataHandler = json.NewAttachHandler() + metadataHandler = json.NewAttachHandler(out) default: - metadataHandler = template.NewAttachHandler(format) + metadataHandler = template.NewAttachHandler(format, out) } - metadataHandler.WithOutput(out) + return statusHandler, metadataHandler } diff --git a/cmd/oras/internal/display/metadata/interface.go b/cmd/oras/internal/display/metadata/interface.go index 9459a5fb6..577f9eb2e 100644 --- a/cmd/oras/internal/display/metadata/interface.go +++ b/cmd/oras/internal/display/metadata/interface.go @@ -17,7 +17,6 @@ package metadata import ( ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "oras.land/oras/cmd/oras/internal/display/metadata/view" "oras.land/oras/cmd/oras/internal/option" ) @@ -25,11 +24,9 @@ import ( type PushHandler interface { OnCopied(opts *option.Target) error OnCompleted(root ocispec.Descriptor) error - view.Outputable } // AttachHandler handles metadata output for attach events. type AttachHandler interface { OnCompleted(opts *option.Target, root, subject ocispec.Descriptor) error - view.Outputable } diff --git a/cmd/oras/internal/display/metadata/json/attach.go b/cmd/oras/internal/display/metadata/json/attach.go index 90ff3486a..3a864cde2 100644 --- a/cmd/oras/internal/display/metadata/json/attach.go +++ b/cmd/oras/internal/display/metadata/json/attach.go @@ -16,26 +16,27 @@ limitations under the License. package json import ( + "io" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" "oras.land/oras/cmd/oras/internal/display/metadata" "oras.land/oras/cmd/oras/internal/display/metadata/model" - "oras.land/oras/cmd/oras/internal/display/metadata/view" "oras.land/oras/cmd/oras/internal/option" ) // AttachHandler handles json metadata output for attach events. type AttachHandler struct { - view.Printer + out io.Writer } // NewAttachHandler creates a new handler for attach events. -func NewAttachHandler() metadata.AttachHandler { +func NewAttachHandler(out io.Writer) metadata.AttachHandler { return &AttachHandler{ - Printer: view.NewPrinter(), + out: out, } } // OnCompleted is called when the attach command is completed. func (a *AttachHandler) OnCompleted(opts *option.Target, root, subject ocispec.Descriptor) error { - return a.PrintJSON(model.NewPush(root, opts.Path)) + return printJSON(a.out, model.NewPush(root, opts.Path)) } diff --git a/cmd/oras/internal/display/metadata/json/json.go b/cmd/oras/internal/display/metadata/json/json.go new file mode 100644 index 000000000..ec3fde8ae --- /dev/null +++ b/cmd/oras/internal/display/metadata/json/json.go @@ -0,0 +1,27 @@ +/* +Copyright The ORAS Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package json + +import ( + "encoding/json" + "io" +) + +func printJSON(out io.Writer, object any) error { + encoder := json.NewEncoder(out) + encoder.SetIndent("", " ") + return encoder.Encode(object) +} diff --git a/cmd/oras/internal/display/metadata/json/push.go b/cmd/oras/internal/display/metadata/json/push.go index 1c9328bda..7781e5cf8 100644 --- a/cmd/oras/internal/display/metadata/json/push.go +++ b/cmd/oras/internal/display/metadata/json/push.go @@ -16,23 +16,24 @@ limitations under the License. package json import ( + "io" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" "oras.land/oras/cmd/oras/internal/display/metadata" "oras.land/oras/cmd/oras/internal/display/metadata/model" - "oras.land/oras/cmd/oras/internal/display/metadata/view" "oras.land/oras/cmd/oras/internal/option" ) // PushHandler handles JSON metadata output for push events. type PushHandler struct { path string - view.Printer + out io.Writer } // NewPushHandler creates a new handler for push events. -func NewPushHandler() metadata.PushHandler { +func NewPushHandler(out io.Writer) metadata.PushHandler { return &PushHandler{ - Printer: view.NewPrinter(), + out: out, } } @@ -44,5 +45,5 @@ func (ph *PushHandler) OnCopied(opts *option.Target) error { // OnCompleted is called after the push is completed. func (ph *PushHandler) OnCompleted(root ocispec.Descriptor) error { - return ph.PrintJSON(model.NewPush(root, ph.path)) + return printJSON(ph.out, model.NewPush(root, ph.path)) } diff --git a/cmd/oras/internal/display/metadata/template/attach.go b/cmd/oras/internal/display/metadata/template/attach.go index 907c569e0..49cd4706c 100644 --- a/cmd/oras/internal/display/metadata/template/attach.go +++ b/cmd/oras/internal/display/metadata/template/attach.go @@ -16,28 +16,29 @@ limitations under the License. package template import ( + "io" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" "oras.land/oras/cmd/oras/internal/display/metadata" "oras.land/oras/cmd/oras/internal/display/metadata/model" - "oras.land/oras/cmd/oras/internal/display/metadata/view" "oras.land/oras/cmd/oras/internal/option" ) // AttachHandler handles go-template metadata output for attach events. type AttachHandler struct { template string - view.Printer + out io.Writer } // NewAttachHandler returns a new handler for attach metadata events. -func NewAttachHandler(template string) metadata.AttachHandler { +func NewAttachHandler(template string, out io.Writer) metadata.AttachHandler { return &AttachHandler{ - Printer: view.NewPrinter(), + out: out, template: template, } } // OnCompleted formats the metadata of attach command. func (ah *AttachHandler) OnCompleted(opts *option.Target, root, subject ocispec.Descriptor) error { - return ah.ParseAndWrite(model.NewPush(root, opts.Path), ah.template) + return parseAndWrite(ah.out, model.NewPush(root, opts.Path), ah.template) } diff --git a/cmd/oras/internal/display/metadata/template/push.go b/cmd/oras/internal/display/metadata/template/push.go index d6c5c1cac..46adeb538 100644 --- a/cmd/oras/internal/display/metadata/template/push.go +++ b/cmd/oras/internal/display/metadata/template/push.go @@ -16,10 +16,11 @@ limitations under the License. package template import ( + "io" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" "oras.land/oras/cmd/oras/internal/display/metadata" "oras.land/oras/cmd/oras/internal/display/metadata/model" - "oras.land/oras/cmd/oras/internal/display/metadata/view" "oras.land/oras/cmd/oras/internal/option" ) @@ -27,13 +28,13 @@ import ( type PushHandler struct { template string path string - view.Printer + out io.Writer } // NewPushHandler returns a new handler for push events. -func NewPushHandler(template string) metadata.PushHandler { +func NewPushHandler(template string, out io.Writer) metadata.PushHandler { return &PushHandler{ - Printer: view.NewPrinter(), + out: out, template: template, } } @@ -46,5 +47,5 @@ func (ph *PushHandler) OnCopied(opts *option.Target) error { // OnCompleted is called after the push is completed. func (ph *PushHandler) OnCompleted(root ocispec.Descriptor) error { - return ph.ParseAndWrite(model.NewPush(root, ph.path), ph.template) + return parseAndWrite(ph.out, model.NewPush(root, ph.path), ph.template) } diff --git a/cmd/oras/internal/display/metadata/template/template.go b/cmd/oras/internal/display/metadata/template/template.go new file mode 100644 index 000000000..151a5d1d9 --- /dev/null +++ b/cmd/oras/internal/display/metadata/template/template.go @@ -0,0 +1,31 @@ +/* +Copyright The ORAS Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package template + +import ( + "io" + "text/template" + + "github.com/Masterminds/sprig/v3" +) + +func parseAndWrite(out io.Writer, object any, templateStr string) error { + t, err := template.New("format output").Funcs(sprig.FuncMap()).Parse(templateStr) + if err != nil { + return err + } + return t.Execute(out, object) +} diff --git a/cmd/oras/internal/display/metadata/text/attach.go b/cmd/oras/internal/display/metadata/text/attach.go index 94a388400..f98d2cf78 100644 --- a/cmd/oras/internal/display/metadata/text/attach.go +++ b/cmd/oras/internal/display/metadata/text/attach.go @@ -17,23 +17,23 @@ package text import ( "fmt" + "io" "strings" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "oras.land/oras/cmd/oras/internal/display/metadata" - "oras.land/oras/cmd/oras/internal/display/metadata/view" "oras.land/oras/cmd/oras/internal/option" ) // AttachHandler handles text metadata output for attach events. type AttachHandler struct { - view.Printer + out io.Writer } // NewAttachHandler returns a new handler for attach events. -func NewAttachHandler() metadata.AttachHandler { +func NewAttachHandler(out io.Writer) metadata.AttachHandler { return &AttachHandler{ - Printer: view.NewPrinter(), + out: out, } } @@ -43,10 +43,10 @@ func (a *AttachHandler) OnCompleted(opts *option.Target, root, subject ocispec.D if !strings.HasSuffix(opts.RawReference, digest) { opts.RawReference = fmt.Sprintf("%s@%s", opts.Path, subject.Digest) } - _, err := a.Println("Attached to", opts.AnnotatedReference()) + _, err := fmt.Fprintln(a.out, "Attached to", opts.AnnotatedReference()) if err != nil { return err } - _, err = a.Println("Digest:", root.Digest) + _, err = fmt.Fprintln(a.out, "Digest:", root.Digest) return err } diff --git a/cmd/oras/internal/display/metadata/text/push.go b/cmd/oras/internal/display/metadata/text/push.go index 6498755a0..c6581d786 100644 --- a/cmd/oras/internal/display/metadata/text/push.go +++ b/cmd/oras/internal/display/metadata/text/push.go @@ -16,32 +16,34 @@ limitations under the License. package text import ( + "fmt" + "io" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" "oras.land/oras/cmd/oras/internal/display/metadata" - "oras.land/oras/cmd/oras/internal/display/metadata/view" "oras.land/oras/cmd/oras/internal/option" ) // PushHandler handles text metadata output for push events. type PushHandler struct { - view.Printer + out io.Writer } // NewPushHandler returns a new handler for push events. -func NewPushHandler() metadata.PushHandler { +func NewPushHandler(out io.Writer) metadata.PushHandler { return &PushHandler{ - Printer: view.NewPrinter(), + out: out, } } // OnCopied is called after files are copied. func (p *PushHandler) OnCopied(opts *option.Target) error { - _, err := p.Println("Pushed", opts.AnnotatedReference()) + _, err := fmt.Fprintln(p.out, "Pushed", opts.AnnotatedReference()) return err } // OnCompleted is called after the push is completed. func (p *PushHandler) OnCompleted(root ocispec.Descriptor) error { - _, err := p.Println("Digest:", root.Digest) + _, err := fmt.Fprintln(p.out, "Digest:", root.Digest) return err } diff --git a/cmd/oras/internal/display/metadata/view/printer.go b/cmd/oras/internal/display/metadata/view/printer.go deleted file mode 100644 index 2b368b12c..000000000 --- a/cmd/oras/internal/display/metadata/view/printer.go +++ /dev/null @@ -1,80 +0,0 @@ -/* -Copyright The ORAS Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package view - -import ( - "encoding/json" - "fmt" - "html/template" - "io" - - "github.com/Masterminds/sprig/v3" -) - -// Outputable interface is used to reset output of the handler. -type Outputable interface { - // WithOutput resets output of the handler. - WithOutput(out io.Writer) -} - -// Printer prints. -type Printer interface { - Printf(format string, a ...any) (n int, err error) - Println(a ...any) (n int, err error) - PrintJSON(object any) error - ParseAndWrite(object any, templateStr string) error - Outputable -} - -type printer struct { - out io.Writer -} - -// NewPrinter creates a new printer based on out. -func NewPrinter() Printer { - return &printer{} -} - -// Printf writes the formatted string to the out. -func (p *printer) Printf(format string, a ...any) (n int, err error) { - return fmt.Fprintf(p.out, format, a...) -} - -// Println writes the string to the out. -func (p *printer) Println(a ...any) (n int, err error) { - return fmt.Fprintln(p.out, a...) -} - -// PrintJSON writes the object as JSON to the out. -func (p *printer) PrintJSON(object any) error { - encoder := json.NewEncoder(p.out) - encoder.SetIndent("", " ") - return encoder.Encode(object) -} - -// ParseAndWrite parses the template string and writes to the out. -func (p *printer) ParseAndWrite(object any, templateStr string) error { - t, err := template.New("format output").Funcs(sprig.FuncMap()).Parse(templateStr) - if err != nil { - return err - } - return t.Execute(p.out, object) -} - -// WithOutput implements metadata.Outputer. -func (p *printer) WithOutput(out io.Writer) { - p.out = out -}