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  }