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 }