github.com/cspotcode/docker-cli@v20.10.0-rc1.0.20201201121459-3faad7acc5b8+incompatible/cli/command/formatter/formatter.go (about)

     1  package formatter
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"strings"
     7  	"text/tabwriter"
     8  	"text/template"
     9  
    10  	"github.com/docker/cli/templates"
    11  	"github.com/pkg/errors"
    12  )
    13  
    14  // Format keys used to specify certain kinds of output formats
    15  const (
    16  	TableFormatKey  = "table"
    17  	RawFormatKey    = "raw"
    18  	PrettyFormatKey = "pretty"
    19  
    20  	DefaultQuietFormat = "{{.ID}}"
    21  )
    22  
    23  // Format is the format string rendered using the Context
    24  type Format string
    25  
    26  // IsTable returns true if the format is a table-type format
    27  func (f Format) IsTable() bool {
    28  	return strings.HasPrefix(string(f), TableFormatKey)
    29  }
    30  
    31  // Contains returns true if the format contains the substring
    32  func (f Format) Contains(sub string) bool {
    33  	return strings.Contains(string(f), sub)
    34  }
    35  
    36  // Context contains information required by the formatter to print the output as desired.
    37  type Context struct {
    38  	// Output is the output stream to which the formatted string is written.
    39  	Output io.Writer
    40  	// Format is used to choose raw, table or custom format for the output.
    41  	Format Format
    42  	// Trunc when set to true will truncate the output of certain fields such as Container ID.
    43  	Trunc bool
    44  
    45  	// internal element
    46  	finalFormat string
    47  	header      interface{}
    48  	buffer      *bytes.Buffer
    49  }
    50  
    51  func (c *Context) preFormat() {
    52  	c.finalFormat = string(c.Format)
    53  
    54  	// TODO: handle this in the Format type
    55  	if c.Format.IsTable() {
    56  		c.finalFormat = c.finalFormat[len(TableFormatKey):]
    57  	}
    58  
    59  	c.finalFormat = strings.Trim(c.finalFormat, " ")
    60  	r := strings.NewReplacer(`\t`, "\t", `\n`, "\n")
    61  	c.finalFormat = r.Replace(c.finalFormat)
    62  }
    63  
    64  func (c *Context) parseFormat() (*template.Template, error) {
    65  	tmpl, err := templates.Parse(c.finalFormat)
    66  	if err != nil {
    67  		return tmpl, errors.Errorf("Template parsing error: %v\n", err)
    68  	}
    69  	return tmpl, err
    70  }
    71  
    72  func (c *Context) postFormat(tmpl *template.Template, subContext SubContext) {
    73  	if c.Format.IsTable() {
    74  		t := tabwriter.NewWriter(c.Output, 10, 1, 3, ' ', 0)
    75  		buffer := bytes.NewBufferString("")
    76  		tmpl.Funcs(templates.HeaderFunctions).Execute(buffer, subContext.FullHeader())
    77  		buffer.WriteTo(t)
    78  		t.Write([]byte("\n"))
    79  		c.buffer.WriteTo(t)
    80  		t.Flush()
    81  	} else {
    82  		c.buffer.WriteTo(c.Output)
    83  	}
    84  }
    85  
    86  func (c *Context) contextFormat(tmpl *template.Template, subContext SubContext) error {
    87  	if err := tmpl.Execute(c.buffer, subContext); err != nil {
    88  		return errors.Errorf("Template parsing error: %v\n", err)
    89  	}
    90  	if c.Format.IsTable() && c.header != nil {
    91  		c.header = subContext.FullHeader()
    92  	}
    93  	c.buffer.WriteString("\n")
    94  	return nil
    95  }
    96  
    97  // SubFormat is a function type accepted by Write()
    98  type SubFormat func(func(SubContext) error) error
    99  
   100  // Write the template to the buffer using this Context
   101  func (c *Context) Write(sub SubContext, f SubFormat) error {
   102  	c.buffer = bytes.NewBufferString("")
   103  	c.preFormat()
   104  
   105  	tmpl, err := c.parseFormat()
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	subFormat := func(subContext SubContext) error {
   111  		return c.contextFormat(tmpl, subContext)
   112  	}
   113  	if err := f(subFormat); err != nil {
   114  		return err
   115  	}
   116  
   117  	c.postFormat(tmpl, sub)
   118  	return nil
   119  }