github.com/SamarSidharth/kpt@v0.0.0-20231122062228-c7d747ae3ace/pkg/printer/printer.go (about) 1 // Copyright 2021 The kpt Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package printer defines utilities to display kpt CLI output. 16 package printer 17 18 import ( 19 "context" 20 "fmt" 21 "io" 22 "os" 23 24 "github.com/GoogleContainerTools/kpt/internal/pkg" 25 "github.com/GoogleContainerTools/kpt/internal/types" 26 ) 27 28 // TruncateOutput defines should output be truncated 29 var TruncateOutput bool 30 31 // Printer defines capabilities to display content in kpt CLI. 32 // The main intention, at the moment, is to abstract away printing 33 // output in the CLI so that we can evolve the kpt CLI UX. 34 type Printer interface { 35 PrintPackage(pkg *pkg.Pkg, leadingNewline bool) 36 Printf(format string, args ...interface{}) 37 OptPrintf(opt *Options, format string, args ...interface{}) 38 OutStream() io.Writer 39 ErrStream() io.Writer 40 } 41 42 // Options are optional options for printer 43 type Options struct { 44 // PkgPath is the unique path to the package 45 PkgPath types.UniquePath 46 // PkgDisplayPath is the display path for the package 47 PkgDisplayPath types.DisplayPath 48 } 49 50 // NewOpt returns a pointer to new options 51 func NewOpt() *Options { 52 return &Options{} 53 } 54 55 // Pkg sets the package unique path in options 56 func (opt *Options) Pkg(p types.UniquePath) *Options { 57 opt.PkgPath = p 58 return opt 59 } 60 61 // PkgDisplayPath sets the package display path in options 62 func (opt *Options) PkgDisplay(p types.DisplayPath) *Options { 63 opt.PkgDisplayPath = p 64 return opt 65 } 66 67 // New returns an instance of Printer. 68 func New(outStream, errStream io.Writer) Printer { 69 if outStream == nil { 70 outStream = os.Stdout 71 } 72 if errStream == nil { 73 errStream = os.Stderr 74 } 75 return &printer{ 76 outStream: outStream, 77 errStream: errStream, 78 } 79 } 80 81 // printer implements default Printer to be used in kpt codebase. 82 type printer struct { 83 outStream io.Writer 84 errStream io.Writer 85 } 86 87 // The key type is unexported to prevent collisions with context keys defined in 88 // other packages. 89 type contextKey int 90 91 // printerKey is the context key for the printer. Its value of zero is 92 // arbitrary. If this package defined other context keys, they would have 93 // different integer values. 94 const printerKey contextKey = 0 95 96 // OutStream returns the StdOut stream, this can be used by callers to print 97 // command output to stdout, do not print error/debug logs to this stream 98 func (pr *printer) OutStream() io.Writer { 99 return pr.outStream 100 } 101 102 // ErrStream returns the StdErr stream, this can be used by callers to print 103 // command output to stderr, print only error/debug/info logs to this stream 104 func (pr *printer) ErrStream() io.Writer { 105 return pr.errStream 106 } 107 108 // PrintPackage prints the package display path to stderr 109 func (pr *printer) PrintPackage(p *pkg.Pkg, leadingNewline bool) { 110 if leadingNewline { 111 fmt.Fprint(pr.errStream, "\n") 112 } 113 fmt.Fprintf(pr.errStream, "Package %q:\n", p.DisplayPath) 114 } 115 116 // Printf is the wrapper over fmt.Printf that displays the output. 117 // this will print messages to stderr stream 118 func (pr *printer) Printf(format string, args ...interface{}) { 119 fmt.Fprintf(pr.errStream, format, args...) 120 } 121 122 // OptPrintf is the wrapper over fmt.Printf that displays the output according 123 // to the opt, this will print messages to stderr stream 124 // https://mehulkar.com/blog/2017/11/stdout-vs-stderr/ 125 func (pr *printer) OptPrintf(opt *Options, format string, args ...interface{}) { 126 if opt == nil { 127 fmt.Fprintf(pr.errStream, format, args...) 128 return 129 } 130 o := pr.errStream 131 if !opt.PkgDisplayPath.Empty() { 132 format = fmt.Sprintf("Package %q: ", string(opt.PkgDisplayPath)) + format 133 } else if !opt.PkgPath.Empty() { 134 // try to print relative path of the pkg if we can else use abs path 135 relPath, err := opt.PkgPath.RelativePath() 136 if err != nil { 137 relPath = string(opt.PkgPath) 138 } 139 format = fmt.Sprintf("Package %q: ", relPath) + format 140 } 141 fmt.Fprintf(o, format, args...) 142 } 143 144 // Helper functions to set and retrieve printer instance from a context. 145 // Defining them here avoids the context key collision. 146 147 // FromContext returns printer instance associated with the context. 148 func FromContextOrDie(ctx context.Context) Printer { 149 pr, ok := ctx.Value(printerKey).(Printer) 150 if ok { 151 return pr 152 } 153 panic("printer missing in context") 154 } 155 156 // WithContext creates new context from the given parent context 157 // by setting the printer instance. 158 func WithContext(ctx context.Context, pr Printer) context.Context { 159 return context.WithValue(ctx, printerKey, pr) 160 }