github.com/olljanat/moby@v1.13.1/cli/command/formatter/formatter.go (about)

     1  package formatter
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"strings"
     8  	"text/tabwriter"
     9  	"text/template"
    10  
    11  	"github.com/docker/docker/utils/templates"
    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      string
    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, fmt.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  		if len(c.header) == 0 {
    75  			// if we still don't have a header, we didn't have any containers so we need to fake it to get the right headers from the template
    76  			tmpl.Execute(bytes.NewBufferString(""), subContext)
    77  			c.header = subContext.FullHeader()
    78  		}
    79  
    80  		t := tabwriter.NewWriter(c.Output, 20, 1, 3, ' ', 0)
    81  		t.Write([]byte(c.header))
    82  		t.Write([]byte("\n"))
    83  		c.buffer.WriteTo(t)
    84  		t.Flush()
    85  	} else {
    86  		c.buffer.WriteTo(c.Output)
    87  	}
    88  }
    89  
    90  func (c *Context) contextFormat(tmpl *template.Template, subContext subContext) error {
    91  	if err := tmpl.Execute(c.buffer, subContext); err != nil {
    92  		return fmt.Errorf("Template parsing error: %v\n", err)
    93  	}
    94  	if c.Format.IsTable() && len(c.header) == 0 {
    95  		c.header = subContext.FullHeader()
    96  	}
    97  	c.buffer.WriteString("\n")
    98  	return nil
    99  }
   100  
   101  // SubFormat is a function type accepted by Write()
   102  type SubFormat func(func(subContext) error) error
   103  
   104  // Write the template to the buffer using this Context
   105  func (c *Context) Write(sub subContext, f SubFormat) error {
   106  	c.buffer = bytes.NewBufferString("")
   107  	c.preFormat()
   108  
   109  	tmpl, err := c.parseFormat()
   110  	if err != nil {
   111  		return err
   112  	}
   113  
   114  	subFormat := func(subContext subContext) error {
   115  		return c.contextFormat(tmpl, subContext)
   116  	}
   117  	if err := f(subFormat); err != nil {
   118  		return err
   119  	}
   120  
   121  	c.postFormat(tmpl, sub)
   122  	return nil
   123  }